1
1
/**
2
2
* @name User Input to Invoke-Expression
3
- * @description Finding cases where the user input is passed an Invoke-Expression command
3
+ * @description Finding cases where the user input is passed an dangerous method that can lead to RCE
4
4
* @kind path-problem
5
5
* @problem.severity error
6
6
* @security-severity 9.8
@@ -15,14 +15,17 @@ import powershell
15
15
import semmle.code.powershell.dataflow.TaintTracking
16
16
import semmle.code.powershell.dataflow.DataFlow
17
17
import semmle.code.powershell.ApiGraphs
18
+ import semmle.code.powershell.dataflow.flowsources.FlowSources
19
+
20
+ import Sanitizers
18
21
19
22
private module TestConfig implements DataFlow:: ConfigSig {
20
23
predicate isSource ( DataFlow:: Node source ) {
21
- exists ( CmdCall c |
22
- c . getName ( ) = "Read-Host" and
23
- source . asExpr ( ) . getExpr ( ) = c ) }
24
+ source instanceof SourceNode or
25
+ source instanceof Source
26
+ }
24
27
25
- predicate isSink ( DataFlow:: Node sink ) { any ( ) } // sink instanceof Sink }
28
+ predicate isSink ( DataFlow:: Node sink ) { sink instanceof Sink }
26
29
predicate isBarrier ( DataFlow:: Node node ) { node instanceof Sanitizer }
27
30
}
28
31
@@ -65,22 +68,19 @@ class InvokeExpressionCall extends Sink {
65
68
66
69
class InvokeScriptSink extends Sink {
67
70
InvokeScriptSink ( ) {
68
- exists ( InvokeMemberExpr ie |
69
- this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
70
- ie .getName ( ) = "InvokeScript" and
71
- ie .getQualifier ( ) .toString ( ) = "InvokeCommand" and
72
- ie .getQualifier ( ) .getAChild ( ) .toString ( ) = "executioncontext"
71
+ exists ( API:: Node call |
72
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "invokescript" ) = call and
73
+ this = call .getArgument ( _) .asSink ( )
73
74
)
74
75
}
75
76
}
76
77
77
78
class CreateNestedPipelineSink extends Sink {
78
79
CreateNestedPipelineSink ( ) {
79
- exists ( InvokeMemberExpr ie |
80
- this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
81
- ie .getName ( ) = "CreateNestedPipeline" and
82
- ie .getQualifier ( ) .toString ( ) = "InvokeCommand" and
83
- ie .getQualifier ( ) .getAChild ( ) .toString ( ) = "executioncontext" )
80
+ exists ( API:: Node call |
81
+ API:: getTopLevelMember ( "host" ) .getMember ( "runspace" ) .getMethod ( "createnestedpipeline" ) = call and
82
+ this = call .getArgument ( _) .asSink ( )
83
+ )
84
84
}
85
85
}
86
86
@@ -96,35 +96,105 @@ class AddScriptInvokeSink extends Sink {
96
96
}
97
97
}
98
98
99
- abstract class Sanitizer extends DataFlow:: Node { }
100
-
101
- // class TypedParameterSanitizer extends Sanitizer {
102
- // TypedParameterSanitizer() {
103
- // exists(Function f, Parameter p |
104
- // p = f.getAParameter() and
105
- // p.getStaticType() != "Object" and
106
- // this.asParameter() = p
107
- // )
108
- // }
109
- // }
110
-
111
- // class SingleQuoteSanitizer extends Sanitizer {
112
- // SingleQuoteSanitizer() {
113
- // exists(Expr e, VarReadAccess v |
114
- // e = this.asExpr().getExpr().getParent() and
115
- // e.toString().matches("%'$" + v.getVariable().getName() + "'%")
116
- // )
117
- // }
118
- // }
99
+ class PowershellSink extends Sink {
100
+ PowershellSink ( ) {
101
+ exists ( CmdCall c |
102
+ c .getName ( ) = "powershell" |
103
+ (
104
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
105
+ c .getArgument ( 0 ) .getValue ( ) .toString ( ) = "-command"
106
+ ) or
107
+ (
108
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 0 )
109
+ )
110
+ )
111
+ }
112
+ }
113
+
114
+ class CmdSink extends Sink {
115
+ CmdSink ( ) {
116
+ exists ( CmdCall c |
117
+ this .asExpr ( ) .getExpr ( ) = c .getArgument ( 1 ) and
118
+ c .getName ( ) = "cmd" and
119
+ c .getArgument ( 0 ) .getValue ( ) .toString ( ) = "/c"
120
+ )
121
+ }
122
+ }
123
+
124
+ class ForEachObjectSink extends Sink {
125
+ ForEachObjectSink ( ) {
126
+ exists ( CmdCall c |
127
+ this .asExpr ( ) .getExpr ( ) = c .getAnArgument ( ) and
128
+ c .getName ( ) = "Foreach-Object"
129
+ )
130
+ }
131
+ }
132
+
133
+ class InvokeSink extends Sink {
134
+ InvokeSink ( ) {
135
+ exists ( InvokeMemberExpr ie |
136
+ this .asExpr ( ) .getExpr ( ) = ie .getCallee ( ) or
137
+ this .asExpr ( ) .getExpr ( ) = ie .getQualifier ( ) .getAChild * ( )
138
+ )
139
+ }
140
+ }
141
+
142
+ class CreateScriptBlockSink extends Sink {
143
+ CreateScriptBlockSink ( ) {
144
+ exists ( InvokeMemberExpr ie |
145
+ this .asExpr ( ) .getExpr ( ) = ie .getAnArgument ( ) and
146
+ ie .getName ( ) = "Create" and
147
+ ie .getQualifier ( ) .toString ( ) = "ScriptBlock"
148
+ )
149
+ }
150
+ }
151
+
152
+ class NewScriptBlockSink extends Sink {
153
+ NewScriptBlockSink ( ) {
154
+ exists ( API:: Node call |
155
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "newscriptblock" ) = call and
156
+ this = call .getArgument ( _) .asSink ( )
157
+ )
158
+ }
159
+ }
160
+
161
+ class ExpandStringSink extends Sink {
162
+ ExpandStringSink ( ) {
163
+ exists ( API:: Node call | this = call .getArgument ( _) .asSink ( ) |
164
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call or
165
+ API:: getTopLevelMember ( "executioncontext" ) .getMember ( "sessionstate" ) .getMember ( "invokecommand" ) .getMethod ( "expandstring" ) = call
166
+
167
+ )
168
+ }
169
+ }
119
170
120
171
module TestFlow = TaintTracking:: Global< TestConfig > ;
121
172
import TestFlow:: PathGraph
122
173
123
174
from TestFlow:: PathNode source , TestFlow:: PathNode sink
124
175
where
125
- TestFlow:: flowPath ( source , sink ) and
126
- sink .getNode ( ) .asExpr ( ) .getExpr ( ) .getLocation ( ) .getFile ( ) .getBaseName ( ) = "sanitizers.ps1"
127
- select sink .getNode ( ) , source , sink , "Flow from user input to Invoke-Expression"
176
+ TestFlow:: flowPath ( source , sink )
177
+ select sink .getNode ( ) , source , sink , "Flow from user input to dangerous method"
178
+
179
+ // from CmdCall c
180
+ // where c.getName() = "cmd"
181
+ // and c.getArgument(0).getValue().toString() = "/c"
182
+ // select c.getArgument(1)
183
+
184
+ // from InvokeMemberExpr ie
185
+ // where ie.getName() = "Create" and
186
+ // ie.getQualifier().toString() = "ScriptBlock"
187
+ // select ie, ie.getQualifier(), ie.getAnArgument()
188
+
189
+ // from API::Node call
190
+ // where API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("newscriptblock") = call
191
+ // select call, call.getArgument(_).asSink()
192
+
193
+ // from Expr e
194
+ // where e.getLocation().getFile().getBaseName() = "InjectionHunterTests.ps1"
195
+ // and e.getLocation().getStartLine() = 106
196
+ // select e, e.getAQlClass()
197
+
128
198
129
199
// from Function f, CmdCall c
130
200
// where f.getLocation().getFile().getBaseName() = "sanitizers.ps1"
0 commit comments