1
- /** Provides class and predicates to track external data that
1
+ /**
2
+ * Provides class and predicates to track external data that
2
3
* may represent malicious OS commands.
3
4
*
4
5
* This module is intended to be imported into a taint-tracking query
5
6
* to extend `TaintKind` and `TaintSink`.
6
- *
7
7
*/
8
- import python
9
8
9
+ import python
10
10
import semmle.python.security.TaintTracking
11
11
import semmle.python.security.strings.Untrusted
12
12
13
+ /** Abstract taint sink that is potentially vulnerable to malicious shell commands. */
14
+ abstract class CommandSink extends TaintSink { }
13
15
14
16
private ModuleObject osOrPopenModule ( ) {
15
17
result .getName ( ) = "os" or
16
18
result .getName ( ) = "popen2"
17
19
}
18
20
19
21
private Object makeOsCall ( ) {
20
- exists ( string name |
21
- result = ModuleObject:: named ( "subprocess" ) .attr ( name ) |
22
+ exists ( string name | result = ModuleObject:: named ( "subprocess" ) .attr ( name ) |
22
23
name = "Popen" or
23
- name = "call" or
24
+ name = "call" or
24
25
name = "check_call" or
25
26
name = "check_output" or
26
27
name = "run"
@@ -29,40 +30,27 @@ private Object makeOsCall() {
29
30
30
31
/**Special case for first element in sequence. */
31
32
class FirstElementKind extends TaintKind {
33
+ FirstElementKind ( ) { this = "sequence[" + any ( ExternalStringKind key ) + "][0]" }
32
34
33
- FirstElementKind ( ) {
34
- this = "sequence[" + any ( ExternalStringKind key ) + "][0]"
35
- }
36
-
37
- override string repr ( ) {
38
- result = "first item in sequence of " + this .getItem ( ) .repr ( )
39
- }
35
+ override string repr ( ) { result = "first item in sequence of " + this .getItem ( ) .repr ( ) }
40
36
41
37
/** Gets the taint kind for item in this sequence. */
42
- ExternalStringKind getItem ( ) {
43
- this = "sequence[" + result + "][0]"
44
- }
45
-
38
+ ExternalStringKind getItem ( ) { this = "sequence[" + result + "][0]" }
46
39
}
47
40
48
41
class FirstElementFlow extends DataFlowExtension:: DataFlowNode {
42
+ FirstElementFlow ( ) { this = any ( SequenceNode s ) .getElement ( 0 ) }
49
43
50
- FirstElementFlow ( ) {
51
- this = any ( SequenceNode s ) .getElement ( 0 )
52
- }
53
-
54
- override
55
- ControlFlowNode getASuccessorNode ( TaintKind fromkind , TaintKind tokind ) {
44
+ override ControlFlowNode getASuccessorNode ( TaintKind fromkind , TaintKind tokind ) {
56
45
result .( SequenceNode ) .getElement ( 0 ) = this and tokind .( FirstElementKind ) .getItem ( ) = fromkind
57
46
}
58
-
59
47
}
60
48
61
- /** A taint sink that is potentially vulnerable to malicious shell commands.
49
+ /**
50
+ * A taint sink that is potentially vulnerable to malicious shell commands.
62
51
* The `vuln` in `subprocess.call(shell=vuln)` and similar calls.
63
52
*/
64
- class ShellCommand extends TaintSink {
65
-
53
+ class ShellCommand extends CommandSink {
66
54
override string toString ( ) { result = "shell command" }
67
55
68
56
ShellCommand ( ) {
@@ -75,7 +63,8 @@ class ShellCommand extends TaintSink {
75
63
or
76
64
exists ( CallNode call , string name |
77
65
call .getAnArg ( ) = this and
78
- call .getFunction ( ) .refersTo ( osOrPopenModule ( ) .attr ( name ) ) |
66
+ call .getFunction ( ) .refersTo ( osOrPopenModule ( ) .attr ( name ) )
67
+ |
79
68
name = "system" or
80
69
name = "popen" or
81
70
name .matches ( "popen_" )
@@ -94,19 +83,18 @@ class ShellCommand extends TaintSink {
94
83
/* List (or tuple) containing a tainted string command */
95
84
kind instanceof ExternalStringSequenceKind
96
85
}
97
-
98
86
}
99
87
100
- /** A taint sink that is potentially vulnerable to malicious shell commands.
88
+ /**
89
+ * A taint sink that is potentially vulnerable to malicious shell commands.
101
90
* The `vuln` in `subprocess.call(vuln, ...)` and similar calls.
102
91
*/
103
- class OsCommandFirstArgument extends TaintSink {
104
-
92
+ class OsCommandFirstArgument extends CommandSink {
105
93
override string toString ( ) { result = "OS command first argument" }
106
94
107
95
OsCommandFirstArgument ( ) {
108
96
not this instanceof ShellCommand and
109
- exists ( CallNode call |
97
+ exists ( CallNode call |
110
98
call .getFunction ( ) .refersTo ( makeOsCall ( ) ) and
111
99
call .getArg ( 0 ) = this
112
100
)
@@ -119,5 +107,127 @@ class OsCommandFirstArgument extends TaintSink {
119
107
/* List (or tuple) whose first element is tainted */
120
108
kind instanceof FirstElementKind
121
109
}
110
+ }
111
+
112
+ // -------------------------------------------------------------------------- //
113
+ // Modeling of the 'invoke' package and 'fabric' package (v 2.x)
114
+ //
115
+ // Since fabric build so closely upon invoke, we model them together to avoid
116
+ // duplication
117
+ // -------------------------------------------------------------------------- //
118
+ /**
119
+ * A taint sink that is potentially vulnerable to malicious shell commands.
120
+ * The `vuln` in `invoke.run(vuln, ...)` and similar calls.
121
+ */
122
+ class InvokeRun extends CommandSink {
123
+ InvokeRun ( ) {
124
+ this = Value:: named ( "invoke.run" ) .( FunctionValue ) .getArgumentForCall ( _, 0 )
125
+ or
126
+ this = Value:: named ( "invoke.sudo" ) .( FunctionValue ) .getArgumentForCall ( _, 0 )
127
+ }
128
+
129
+ override string toString ( ) { result = "InvokeRun" }
130
+
131
+ override predicate sinks ( TaintKind kind ) { kind instanceof ExternalStringKind }
132
+ }
133
+
134
+ /**
135
+ * Internal TaintKind to track the invoke.Context instance passed to functions
136
+ * marked with @invoke.task
137
+ */
138
+ private class InvokeContextArg extends TaintKind {
139
+ InvokeContextArg ( ) { this = "InvokeContextArg" }
140
+ }
141
+
142
+ /** Internal TaintSource to track the context passed to functions marked with @invoke.task */
143
+ private class InvokeContextArgSource extends TaintSource {
144
+ InvokeContextArgSource ( ) {
145
+ exists ( Function f , Expr decorator |
146
+ count ( f .getADecorator ( ) ) = 1 and
147
+ (
148
+ decorator = f .getADecorator ( ) and not decorator instanceof Call
149
+ or
150
+ decorator = f .getADecorator ( ) .( Call ) .getFunc ( )
151
+ ) and
152
+ (
153
+ decorator .pointsTo ( Value:: named ( "invoke.task" ) )
154
+ or
155
+ decorator .pointsTo ( Value:: named ( "fabric.task" ) )
156
+ )
157
+ |
158
+ this .( ControlFlowNode ) .getNode ( ) = f .getArg ( 0 )
159
+ )
160
+ }
161
+
162
+ override predicate isSourceOf ( TaintKind kind ) { kind instanceof InvokeContextArg }
163
+ }
164
+
165
+ /**
166
+ * A taint sink that is potentially vulnerable to malicious shell commands.
167
+ * The `vuln` in `invoke.Context().run(vuln, ...)` and similar calls.
168
+ */
169
+ class InvokeContextRun extends CommandSink {
170
+ InvokeContextRun ( ) {
171
+ exists ( CallNode call |
172
+ any ( InvokeContextArg k ) .taints ( call .getFunction ( ) .( AttrNode ) .getObject ( "run" ) )
173
+ or
174
+ call = Value:: named ( "invoke.Context" ) .( ClassValue ) .lookup ( "run" ) .getACall ( )
175
+ or
176
+ // fabric.connection.Connection is a subtype of invoke.context.Context
177
+ // since fabric.Connection.run has a decorator, it doesn't work with FunctionValue :|
178
+ // and `Value::named("fabric.Connection").(ClassValue).lookup("run").getACall()` returned no results,
179
+ // so here is the hacky solution that works :\
180
+ call .getFunction ( ) .( AttrNode ) .getObject ( "run" ) .pointsTo ( ) .getClass ( ) =
181
+ Value:: named ( "fabric.Connection" )
182
+ |
183
+ this = call .getArg ( 0 )
184
+ or
185
+ this = call .getArgByName ( "command" )
186
+ )
187
+ }
188
+
189
+ override string toString ( ) { result = "InvokeContextRun" }
190
+
191
+ override predicate sinks ( TaintKind kind ) { kind instanceof ExternalStringKind }
192
+ }
193
+
194
+ /**
195
+ * A taint sink that is potentially vulnerable to malicious shell commands.
196
+ * The `vuln` in `fabric.Group().run(vuln, ...)` and similar calls.
197
+ */
198
+ class FabricGroupRun extends CommandSink {
199
+ FabricGroupRun ( ) {
200
+ exists ( ClassValue cls |
201
+ cls .getASuperType ( ) = Value:: named ( "fabric.Group" ) and
202
+ this = cls .lookup ( "run" ) .( FunctionValue ) .getArgumentForCall ( _, 1 )
203
+ )
204
+ }
205
+
206
+ override string toString ( ) { result = "FabricGroupRun" }
207
+
208
+ override predicate sinks ( TaintKind kind ) { kind instanceof ExternalStringKind }
209
+ }
210
+
211
+ // -------------------------------------------------------------------------- //
212
+ // Modeling of the 'invoke' package and 'fabric' package (v 1.x)
213
+ // -------------------------------------------------------------------------- //
214
+ class FabricV1Commands extends CommandSink {
215
+ FabricV1Commands ( ) {
216
+ // since `run` and `sudo` are decorated, we can't use FunctionValue's :(
217
+ exists ( CallNode call |
218
+ call = Value:: named ( "fabric.api.local" ) .getACall ( )
219
+ or
220
+ call = Value:: named ( "fabric.api.run" ) .getACall ( )
221
+ or
222
+ call = Value:: named ( "fabric.api.sudo" ) .getACall ( )
223
+ |
224
+ this = call .getArg ( 0 )
225
+ or
226
+ this = call .getArgByName ( "command" )
227
+ )
228
+ }
229
+
230
+ override string toString ( ) { result = "FabricV1Commands" }
122
231
232
+ override predicate sinks ( TaintKind kind ) { kind instanceof ExternalStringKind }
123
233
}
0 commit comments