55 */
66
77private import semmle.code.powershell.dataflow.DataFlow
8+ import semmle.code.powershell.ApiGraphs
89private import semmle.code.powershell.dataflow.flowsources.FlowSources
910private import semmle.code.powershell.Cfg
1011
@@ -20,7 +21,9 @@ module CommandInjection {
2021 /**
2122 * A data flow sink for command-injection vulnerabilities.
2223 */
23- abstract class Sink extends DataFlow:: Node { }
24+ abstract class Sink extends DataFlow:: Node {
25+ abstract string getSinkType ( ) ;
26+ }
2427
2528 /**
2629 * A sanitizer for command-injection vulnerabilities.
@@ -39,13 +42,16 @@ module CommandInjection {
3942 SystemCommandExecutionSink ( ) {
4043 // An argument to a call
4144 exists ( DataFlow:: CallNode call |
42- call .getName ( ) = "Invoke-Expression" and
45+ call .getName ( ) = [ "Invoke-Expression" , "iex" ] and
4346 call .getAnArgument ( ) = this
4447 )
4548 or
4649 // Or the call command itself in case it's a use of operator &.
4750 any ( DataFlow:: CallOperatorNode call ) .getCommand ( ) = this
4851 }
52+ override string getSinkType ( ) {
53+ result = "call to Invoke-Expression"
54+ }
4955 }
5056
5157 class AddTypeSink extends Sink {
@@ -55,11 +61,169 @@ module CommandInjection {
5561 call .getAnArgument ( ) = this
5662 )
5763 }
64+ override string getSinkType ( ) {
65+ result = "call to Add-Type"
66+ }
5867 }
5968
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+
60201 private class ExternalCommandInjectionSink extends Sink {
61202 ExternalCommandInjectionSink ( ) {
62203 this = ModelOutput:: getASinkNode ( "command-injection" ) .asSink ( )
63204 }
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+ }
64227 }
65228}
229+
0 commit comments