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,171 @@ module CommandInjection {
5561 call .getAnArgument ( ) = this
5662 )
5763 }
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"
58106 }
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+ }
59201
60202 private class ExternalCommandInjectionSink extends Sink {
61203 ExternalCommandInjectionSink ( ) {
62204 this = ModelOutput:: getASinkNode ( "command-injection" ) .asSink ( )
63205 }
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+ }
64229 }
65230}
231+
0 commit comments