Skip to content

Commit 52ff5d3

Browse files
authored
Merge pull request #246 from microsoft/powershell-commandinjection-invokesinkfix
InvokeSink fix
2 parents 849e0b4 + 654bf2f commit 52ff5d3

File tree

4 files changed

+29
-32
lines changed

4 files changed

+29
-32
lines changed

powershell/ql/lib/semmle/code/powershell/security/CommandInjectionCustomizations.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,10 @@ module CommandInjection {
142142
class InvokeSink extends Sink {
143143
InvokeSink() {
144144
exists(InvokeMemberExpr ie |
145-
this.asExpr().getExpr() = ie.getCallee() or
146-
this.asExpr().getExpr() = ie.getQualifier().getAChild*()
145+
this.asExpr().getExpr() = ie.getCallee()
146+
or
147+
ie.getAName() = "Invoke" and
148+
ie.getQualifier().(MemberExprReadAccess).getMemberExpr() = this.asExpr().getExpr()
147149
)
148150
}
149151

powershell/ql/src/queries/security/cwe-078/CommandInjection.qhelp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ Injection Hunter:
5858
<!-- LocalWords: CWE untrusted unsanitized Runtime
5959
-->
6060

61+
6162
</references>
6263
</qhelp>

powershell/ql/test/query-tests/security/cwe-078/CommandInjection/CommandInjection.expected

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ edges
33
| test.ps1:9:11:9:20 | userinput | test.ps1:10:9:10:38 | Get-Process -Name $UserInput | provenance | |
44
| test.ps1:15:11:15:20 | userinput | test.ps1:16:50:16:79 | Get-Process -Name $UserInput | provenance | |
55
| test.ps1:21:11:21:20 | userinput | test.ps1:22:41:22:70 | Get-Process -Name $UserInput | provenance | |
6-
| test.ps1:21:11:21:20 | userinput | test.ps1:22:60:22:69 | UserInput | provenance | |
76
| test.ps1:27:11:27:20 | userinput | test.ps1:28:38:28:67 | Get-Process -Name $UserInput | provenance | |
8-
| test.ps1:27:11:27:20 | userinput | test.ps1:28:57:28:66 | UserInput | provenance | |
97
| test.ps1:33:11:33:20 | userinput | test.ps1:34:14:34:46 | public class Foo { $UserInput } | provenance | |
108
| test.ps1:39:11:39:20 | userinput | test.ps1:40:30:40:62 | public class Foo { $UserInput } | provenance | |
119
| test.ps1:45:11:45:20 | userinput | test.ps1:48:30:48:34 | code | provenance | |
@@ -16,7 +14,7 @@ edges
1614
| test.ps1:104:11:104:20 | userinput | test.ps1:108:58:108:87 | Get-Process -Name $UserInput | provenance | |
1715
| test.ps1:114:11:114:20 | userinput | test.ps1:116:34:116:43 | UserInput | provenance | |
1816
| test.ps1:121:11:121:20 | userinput | test.ps1:123:28:123:37 | UserInput | provenance | |
19-
| test.ps1:128:11:128:20 | userinput | test.ps1:130:28:130:37 | UserInput | provenance | |
17+
| test.ps1:129:11:129:20 | userinput | test.ps1:131:28:131:37 | UserInput | provenance | |
2018
| test.ps1:136:11:136:20 | userinput | test.ps1:139:50:139:59 | UserInput | provenance | |
2119
| test.ps1:144:11:144:20 | userinput | test.ps1:147:63:147:72 | UserInput | provenance | |
2220
| test.ps1:152:10:152:32 | Call to read-host | test.ps1:154:46:154:51 | input | provenance | Src:MaD:0 |
@@ -52,7 +50,7 @@ edges
5250
| test.ps1:167:41:167:46 | input | test.ps1:104:11:104:20 | userinput | provenance | |
5351
| test.ps1:168:36:168:41 | input | test.ps1:114:11:114:20 | userinput | provenance | |
5452
| test.ps1:169:36:169:41 | input | test.ps1:121:11:121:20 | userinput | provenance | |
55-
| test.ps1:170:36:170:41 | input | test.ps1:128:11:128:20 | userinput | provenance | |
53+
| test.ps1:170:36:170:41 | input | test.ps1:129:11:129:20 | userinput | provenance | |
5654
| test.ps1:172:42:172:47 | input | test.ps1:136:11:136:20 | userinput | provenance | |
5755
| test.ps1:173:42:173:47 | input | test.ps1:144:11:144:20 | userinput | provenance | |
5856
nodes
@@ -64,10 +62,8 @@ nodes
6462
| test.ps1:16:50:16:79 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
6563
| test.ps1:21:11:21:20 | userinput | semmle.label | userinput |
6664
| test.ps1:22:41:22:70 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
67-
| test.ps1:22:60:22:69 | UserInput | semmle.label | UserInput |
6865
| test.ps1:27:11:27:20 | userinput | semmle.label | userinput |
6966
| test.ps1:28:38:28:67 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
70-
| test.ps1:28:57:28:66 | UserInput | semmle.label | UserInput |
7167
| test.ps1:33:11:33:20 | userinput | semmle.label | userinput |
7268
| test.ps1:34:14:34:46 | public class Foo { $UserInput } | semmle.label | public class Foo { $UserInput } |
7369
| test.ps1:39:11:39:20 | userinput | semmle.label | userinput |
@@ -88,8 +84,8 @@ nodes
8884
| test.ps1:116:34:116:43 | UserInput | semmle.label | UserInput |
8985
| test.ps1:121:11:121:20 | userinput | semmle.label | userinput |
9086
| test.ps1:123:28:123:37 | UserInput | semmle.label | UserInput |
91-
| test.ps1:128:11:128:20 | userinput | semmle.label | userinput |
92-
| test.ps1:130:28:130:37 | UserInput | semmle.label | UserInput |
87+
| test.ps1:129:11:129:20 | userinput | semmle.label | userinput |
88+
| test.ps1:131:28:131:37 | UserInput | semmle.label | UserInput |
9389
| test.ps1:136:11:136:20 | userinput | semmle.label | userinput |
9490
| test.ps1:139:50:139:59 | UserInput | semmle.label | UserInput |
9591
| test.ps1:144:11:144:20 | userinput | semmle.label | userinput |
@@ -119,9 +115,7 @@ subpaths
119115
| test.ps1:10:9:10:38 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:10:9:10:38 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
120116
| test.ps1:16:50:16:79 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:16:50:16:79 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
121117
| test.ps1:22:41:22:70 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:22:41:22:70 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
122-
| test.ps1:22:60:22:69 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:22:60:22:69 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
123118
| test.ps1:28:38:28:67 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:28:38:28:67 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
124-
| test.ps1:28:57:28:66 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:28:57:28:66 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
125119
| test.ps1:34:14:34:46 | public class Foo { $UserInput } | test.ps1:152:10:152:32 | Call to read-host | test.ps1:34:14:34:46 | public class Foo { $UserInput } | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
126120
| test.ps1:40:30:40:62 | public class Foo { $UserInput } | test.ps1:152:10:152:32 | Call to read-host | test.ps1:40:30:40:62 | public class Foo { $UserInput } | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
127121
| test.ps1:48:30:48:34 | code | test.ps1:152:10:152:32 | Call to read-host | test.ps1:48:30:48:34 | code | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
@@ -132,6 +126,6 @@ subpaths
132126
| test.ps1:108:58:108:87 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:108:58:108:87 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
133127
| test.ps1:116:34:116:43 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:116:34:116:43 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
134128
| test.ps1:123:28:123:37 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:123:28:123:37 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
135-
| test.ps1:130:28:130:37 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:130:28:130:37 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
129+
| test.ps1:131:28:131:37 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:131:28:131:37 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
136130
| test.ps1:139:50:139:59 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:139:50:139:59 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
137131
| test.ps1:147:63:147:72 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:147:63:147:72 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |

powershell/ql/test/query-tests/security/cwe-078/CommandInjection/test.ps1

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
11
function Invoke-InvokeExpressionInjection1
22
{
33
param($UserInput)
4-
Invoke-Expression "Get-Process -Name $UserInput"
4+
Invoke-Expression "Get-Process -Name $UserInput" # BAD
55
}
66

77
function Invoke-InvokeExpressionInjection2
88
{
99
param($UserInput)
10-
iex "Get-Process -Name $UserInput"
10+
iex "Get-Process -Name $UserInput" # BAD
1111
}
1212

1313
function Invoke-InvokeExpressionInjection3
1414
{
1515
param($UserInput)
16-
$executionContext.InvokeCommand.InvokeScript("Get-Process -Name $UserInput")
16+
$executionContext.InvokeCommand.InvokeScript("Get-Process -Name $UserInput") # BAD
1717
}
1818

1919
function Invoke-InvokeExpressionInjection4
2020
{
2121
param($UserInput)
22-
$host.Runspace.CreateNestedPipeline("Get-Process -Name $UserInput", $false).Invoke()
22+
$host.Runspace.CreateNestedPipeline("Get-Process -Name $UserInput", $false).Invoke() # BAD
2323
}
2424

2525
function Invoke-InvokeExpressionInjection5
2626
{
2727
param($UserInput)
28-
[PowerShell]::Create().AddScript("Get-Process -Name $UserInput").Invoke()
28+
[PowerShell]::Create().AddScript("Get-Process -Name $UserInput").Invoke() # BAD
2929
}
3030

3131
function Invoke-InvokeExpressionInjection6
3232
{
3333
param($UserInput)
34-
Add-Type "public class Foo { $UserInput }"
34+
Add-Type "public class Foo { $UserInput }" # BAD
3535
}
3636

3737
function Invoke-InvokeExpressionInjection7
3838
{
3939
param($UserInput)
40-
Add-Type -TypeDefinition "public class Foo { $UserInput }"
40+
Add-Type -TypeDefinition "public class Foo { $UserInput }" # BAD
4141
}
4242

4343
function Invoke-InvokeExpressionInjection8
4444
{
4545
param($UserInput)
4646

4747
$code = "public class Foo { $UserInput }"
48-
Add-Type -TypeDefinition $code
48+
Add-Type -TypeDefinition $code # BAD
4949
}
5050

5151
function Invoke-InvokeExpressionInjectionFP
@@ -72,21 +72,21 @@ function Invoke-ExploitableCommandInjection1
7272
{
7373
param($UserInput)
7474

75-
powershell -command "Get-Process -Name $UserInput"
75+
powershell -command "Get-Process -Name $UserInput" # BAD
7676
}
7777

7878
function Invoke-ExploitableCommandInjection2
7979
{
8080
param($UserInput)
8181

82-
powershell "Get-Process -Name $UserInput"
82+
powershell "Get-Process -Name $UserInput" # BAD
8383
}
8484

8585
function Invoke-ExploitableCommandInjection3
8686
{
8787
param($UserInput)
8888

89-
cmd /c "ping $UserInput"
89+
cmd /c "ping $UserInput" # BAD
9090
}
9191

9292
function Invoke-ScriptBlockInjection1
@@ -95,7 +95,7 @@ function Invoke-ScriptBlockInjection1
9595

9696
## Often used when making remote connections
9797

98-
$sb = [ScriptBlock]::Create("Get-Process -Name $UserInput")
98+
$sb = [ScriptBlock]::Create("Get-Process -Name $UserInput") # BAD
9999
Invoke-Command RemoteServer $sb
100100
}
101101

@@ -105,46 +105,46 @@ function Invoke-ScriptBlockInjection2
105105

106106
## Often used when making remote connections
107107

108-
$sb = $executionContext.InvokeCommand.NewScriptBlock("Get-Process -Name $UserInput")
108+
$sb = $executionContext.InvokeCommand.NewScriptBlock("Get-Process -Name $UserInput") # BAD
109109
Invoke-Command RemoteServer $sb
110110
}
111111

112112
function Invoke-MethodInjection1
113113
{
114114
param($UserInput)
115115

116-
Get-Process | Foreach-Object $UserInput
116+
Get-Process | Foreach-Object $UserInput # BAD
117117
}
118118

119119
function Invoke-MethodInjection2
120120
{
121121
param($UserInput)
122122

123-
(Get-Process -Id $pid).$UserInput()
123+
(Get-Process -Id $pid).$UserInput() # BAD
124124
}
125125

126+
126127
function Invoke-MethodInjection3
127128
{
128129
param($UserInput)
129130

130-
(Get-Process -Id $pid).$UserInput.Invoke()
131+
(Get-Process -Id $pid).$UserInput.Invoke() # BAD
131132
}
132133

133-
#TODO: currently a FN
134134
function Invoke-ExpandStringInjection1
135135
{
136136
param($UserInput)
137137

138138
## Used to attempt a variable resolution
139-
$executionContext.InvokeCommand.ExpandString($UserInput)
139+
$executionContext.InvokeCommand.ExpandString($UserInput) # BAD
140140
}
141141

142142
function Invoke-ExpandStringInjection2
143143
{
144144
param($UserInput)
145145

146146
## Used to attempt a variable resolution
147-
$executionContext.SessionState.InvokeCommand.ExpandString($UserInput)
147+
$executionContext.SessionState.InvokeCommand.ExpandString($UserInput) # BAD
148148
}
149149

150150

0 commit comments

Comments
 (0)