5
5
*/
6
6
7
7
private import semmle.code.powershell.dataflow.DataFlow
8
+ import semmle.code.powershell.ApiGraphs
8
9
private import semmle.code.powershell.dataflow.flowsources.FlowSources
9
10
private import semmle.code.powershell.Cfg
10
11
@@ -20,7 +21,9 @@ module CommandInjection {
20
21
/**
21
22
* A data flow sink for command-injection vulnerabilities.
22
23
*/
23
- abstract class Sink extends DataFlow:: Node { }
24
+ abstract class Sink extends DataFlow:: Node {
25
+ abstract string getSinkType ( ) ;
26
+ }
24
27
25
28
/**
26
29
* A sanitizer for command-injection vulnerabilities.
@@ -39,13 +42,16 @@ module CommandInjection {
39
42
SystemCommandExecutionSink ( ) {
40
43
// An argument to a call
41
44
exists ( DataFlow:: CallNode call |
42
- call .getName ( ) = "Invoke-Expression" and
45
+ call .getName ( ) = [ "Invoke-Expression" , "iex" ] and
43
46
call .getAnArgument ( ) = this
44
47
)
45
48
or
46
49
// Or the call command itself in case it's a use of operator &.
47
50
any ( DataFlow:: CallOperatorNode call ) .getCommand ( ) = this
48
51
}
52
+ override string getSinkType ( ) {
53
+ result = "call to Invoke-Expression"
54
+ }
49
55
}
50
56
51
57
class AddTypeSink extends Sink {
@@ -55,11 +61,169 @@ module CommandInjection {
55
61
call .getAnArgument ( ) = this
56
62
)
57
63
}
64
+ override string getSinkType ( ) {
65
+ result = "call to Add-Type"
66
+ }
58
67
}
59
68
69
+ class InvokeScriptSink extends Sink {
70
+ InvokeScriptSink ( ) {
71
+ exists ( API:: Node call |
72
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "invokescript" ) = call and
73
+ this = call .getArgument ( _) .asSink ( )
74
+ )
75
+ }
76
+ override string getSinkType ( ) {
77
+ result = "call to InvokeScript"
78
+ }
79
+ }
80
+
81
+ class CreateNestedPipelineSink extends Sink {
82
+ CreateNestedPipelineSink ( ) {
83
+ exists ( API:: Node call |
84
+ API:: getTopLevelMember ( "host" ) .getMember ( "runspace" ) .getMethod ( "createnestedpipeline" ) = call and
85
+ this = call .getArgument ( _) .asSink ( )
86
+ )
87
+ }
88
+ override string getSinkType ( ) {
89
+ result = "call to CreateNestedPipeline"
90
+ }
91
+ }
92
+
93
+ class AddScriptInvokeSink extends Sink {
94
+ AddScriptInvokeSink ( ) {
95
+ exists ( InvokeMemberExpr ie |
96
+ this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
97
+ ie .getName ( ) = "AddScript" and
98
+ ie .getQualifier ( ) .( InvokeMemberExpr ) .getName ( ) = "Create" and
99
+ ie .getQualifier ( ) .getAChild ( ) .toString ( ) = "PowerShell" and
100
+ ie .getParent ( ) .( InvokeMemberExpr ) .getName ( ) = "Invoke"
101
+ )
102
+ }
103
+ override string getSinkType ( ) {
104
+ result = "call to AddScript"
105
+ }
106
+ }
107
+
108
+ class PowershellSink extends Sink {
109
+ PowershellSink ( ) {
110
+ exists ( CmdCall c |
111
+ c .getName ( ) = "powershell" |
112
+ (
113
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
114
+ c .getArgument ( 0 ) .getValue ( ) .toString ( ) = "-command"
115
+ ) or
116
+ (
117
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 0 )
118
+ )
119
+ )
120
+ }
121
+ override string getSinkType ( ) {
122
+ result = "call to Powershell"
123
+ }
124
+ }
125
+
126
+ class CmdSink extends Sink {
127
+ CmdSink ( ) {
128
+ exists ( CmdCall c |
129
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
130
+ c .getName ( ) = "cmd" and
131
+ c .getArgument ( 0 ) .getValue ( ) .toString ( ) = "/c"
132
+ )
133
+ }
134
+ override string getSinkType ( ) {
135
+ result = "call to Cmd"
136
+ }
137
+ }
138
+
139
+ class ForEachObjectSink extends Sink {
140
+ ForEachObjectSink ( ) {
141
+ exists ( CmdCall c |
142
+ this .asExpr ( ) .getExpr ( ) = c .getAnArgument ( ) and
143
+ c .getName ( ) = "Foreach-Object"
144
+ )
145
+ }
146
+ override string getSinkType ( ) {
147
+ result = "call to ForEach-Object"
148
+ }
149
+ }
150
+
151
+ class InvokeSink extends Sink {
152
+ InvokeSink ( ) {
153
+ exists ( InvokeMemberExpr ie |
154
+ this .asExpr ( ) .getExpr ( ) = ie .getCallee ( ) or
155
+ this .asExpr ( ) .getExpr ( ) = ie .getQualifier ( ) .getAChild * ( )
156
+ )
157
+ }
158
+ override string getSinkType ( ) {
159
+ result = "call to Invoke"
160
+ }
161
+ }
162
+
163
+ class CreateScriptBlockSink extends Sink {
164
+ CreateScriptBlockSink ( ) {
165
+ exists ( InvokeMemberExpr ie |
166
+ this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
167
+ ie .getName ( ) = "Create" and
168
+ ie .getQualifier ( ) .toString ( ) = "ScriptBlock"
169
+ )
170
+ }
171
+ override string getSinkType ( ) {
172
+ result = "call to CreateScriptBlock"
173
+ }
174
+ }
175
+
176
+ class NewScriptBlockSink extends Sink {
177
+ NewScriptBlockSink ( ) {
178
+ exists ( API:: Node call |
179
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "newscriptblock" ) = call and
180
+ this = call .getArgument ( _) .asSink ( )
181
+ )
182
+ }
183
+ override string getSinkType ( ) {
184
+ result = "call to NewScriptBlock"
185
+ }
186
+ }
187
+
188
+ class ExpandStringSink extends Sink {
189
+ ExpandStringSink ( ) {
190
+ exists ( API:: Node call | this = call .getArgument ( _) .asSink ( ) |
191
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call or
192
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "sessionstate" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call
193
+
194
+ )
195
+ }
196
+ override string getSinkType ( ) {
197
+ result = "call to ExpandString"
198
+ }
199
+ }
200
+
60
201
private class ExternalCommandInjectionSink extends Sink {
61
202
ExternalCommandInjectionSink ( ) {
62
203
this = ModelOutput:: getASinkNode ( "command-injection" ) .asSink ( )
63
204
}
205
+ override string getSinkType ( ) {
206
+ result = "external command injection"
207
+ }
208
+ }
209
+
210
+ class TypedParameterSanitizer extends Sanitizer {
211
+ TypedParameterSanitizer ( ) {
212
+ exists ( Function f , Parameter p |
213
+ p = f .getAParameter ( ) and
214
+ p .getStaticType ( ) != "Object" and
215
+ this .asParameter ( ) = p
216
+ )
217
+ }
218
+ }
219
+
220
+ class SingleQuoteSanitizer extends Sanitizer {
221
+ SingleQuoteSanitizer ( ) {
222
+ exists ( Expr e , VarReadAccess v |
223
+ e = this .asExpr ( ) .getExpr ( ) .getParent ( ) and
224
+ e .toString ( ) .matches ( "%'$" + v .getVariable ( ) .getName ( ) + "'%" )
225
+ )
226
+ }
64
227
}
65
228
}
229
+
0 commit comments