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,171 @@ module CommandInjection {
55
61
call .getAnArgument ( ) = this
56
62
)
57
63
}
64
+ override string getSinkType ( ) {
65
+ result = "call to Add-Type"
66
+ }
67
+ }
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 addscript , InvokeMemberExpr create |
96
+ this .asExpr ( ) .getExpr ( ) = addscript .getAnArgument ( ) and
97
+ addscript .getName ( ) = "AddScript" and
98
+ create .getName ( ) = "Create" and
99
+
100
+ addscript .getQualifier ( ) .( InvokeMemberExpr ) = create and
101
+ create .getQualifier ( ) .( TypeNameExpr ) .getName ( ) = "PowerShell"
102
+ )
103
+ }
104
+ override string getSinkType ( ) {
105
+ result = "call to AddScript"
58
106
}
107
+ }
108
+
109
+ class PowershellSink extends Sink {
110
+ PowershellSink ( ) {
111
+ exists ( CmdCall c |
112
+ c .getName ( ) = "powershell" |
113
+ (
114
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
115
+ c .getArgument ( 0 ) .getValue ( ) .asString ( ) = "-command"
116
+ ) or
117
+ (
118
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 0 )
119
+ )
120
+ )
121
+ }
122
+ override string getSinkType ( ) {
123
+ result = "call to Powershell"
124
+ }
125
+ }
126
+
127
+ class CmdSink extends Sink {
128
+ CmdSink ( ) {
129
+ exists ( CmdCall c |
130
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
131
+ c .getName ( ) = "cmd" and
132
+ c .getArgument ( 0 ) .getValue ( ) .asString ( ) = "/c"
133
+ )
134
+ }
135
+ override string getSinkType ( ) {
136
+ result = "call to Cmd"
137
+ }
138
+ }
139
+
140
+ class ForEachObjectSink extends Sink {
141
+ ForEachObjectSink ( ) {
142
+ exists ( CmdCall c |
143
+ this .asExpr ( ) .getExpr ( ) = c .getAnArgument ( ) and
144
+ c .getName ( ) = "Foreach-Object"
145
+ )
146
+ }
147
+ override string getSinkType ( ) {
148
+ result = "call to ForEach-Object"
149
+ }
150
+ }
151
+
152
+ class InvokeSink extends Sink {
153
+ InvokeSink ( ) {
154
+ exists ( InvokeMemberExpr ie |
155
+ this .asExpr ( ) .getExpr ( ) = ie .getCallee ( ) or
156
+ this .asExpr ( ) .getExpr ( ) = ie .getQualifier ( ) .getAChild * ( )
157
+ )
158
+ }
159
+ override string getSinkType ( ) {
160
+ result = "call to Invoke"
161
+ }
162
+ }
163
+
164
+ class CreateScriptBlockSink extends Sink {
165
+ CreateScriptBlockSink ( ) {
166
+ exists ( InvokeMemberExpr ie |
167
+ this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
168
+ ie .getName ( ) = "Create" and
169
+ ie .getQualifier ( ) .( TypeNameExpr ) .getName ( ) = "ScriptBlock"
170
+ )
171
+ }
172
+ override string getSinkType ( ) {
173
+ result = "call to CreateScriptBlock"
174
+ }
175
+ }
176
+
177
+ class NewScriptBlockSink extends Sink {
178
+ NewScriptBlockSink ( ) {
179
+ exists ( API:: Node call |
180
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "newscriptblock" ) = call and
181
+ this = call .getArgument ( _) .asSink ( )
182
+ )
183
+ }
184
+ override string getSinkType ( ) {
185
+ result = "call to NewScriptBlock"
186
+ }
187
+ }
188
+
189
+ class ExpandStringSink extends Sink {
190
+ ExpandStringSink ( ) {
191
+ exists ( API:: Node call | this = call .getArgument ( _) .asSink ( ) |
192
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call or
193
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "sessionstate" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call
194
+
195
+ )
196
+ }
197
+ override string getSinkType ( ) {
198
+ result = "call to ExpandString"
199
+ }
200
+ }
59
201
60
202
private class ExternalCommandInjectionSink extends Sink {
61
203
ExternalCommandInjectionSink ( ) {
62
204
this = ModelOutput:: getASinkNode ( "command-injection" ) .asSink ( )
63
205
}
206
+ override string getSinkType ( ) {
207
+ result = "external command injection"
208
+ }
209
+ }
210
+
211
+ class TypedParameterSanitizer extends Sanitizer {
212
+ TypedParameterSanitizer ( ) {
213
+ exists ( Function f , Parameter p |
214
+ p = f .getAParameter ( ) and
215
+ p .getStaticType ( ) != "Object" and
216
+ this .asParameter ( ) = p
217
+ )
218
+ }
219
+ }
220
+
221
+ class SingleQuoteSanitizer extends Sanitizer {
222
+ SingleQuoteSanitizer ( ) {
223
+ exists ( ExpandableStringExpr e , VarReadAccess v |
224
+ v = this .asExpr ( ) .getExpr ( ) and
225
+ e .getUnexpandedValue ( ) .matches ( "%'$" + v .getVariable ( ) .getName ( ) + "'%" ) and
226
+ e .getAnExpr ( ) = v
227
+ )
228
+ }
64
229
}
65
230
}
231
+
0 commit comments