Skip to content

Commit b94493a

Browse files
committed
Python: Add extra sinks for command-injection query.
1 parent ceb316d commit b94493a

File tree

4 files changed

+32
-6
lines changed

4 files changed

+32
-6
lines changed

python/ql/src/semmle/python/security/injection/Command.qll

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ private ModuleObject subprocessModule() {
1515
result.getName() = "subprocess"
1616
}
1717

18-
private ModuleObject osModule() {
19-
result.getName() = "os"
18+
private ModuleObject osOrPopenModule() {
19+
result.getName() = "os" or
20+
result.getName() = "popen2"
2021
}
2122

2223
private Object makeOsCall() {
@@ -25,7 +26,8 @@ private Object makeOsCall() {
2526
name = "Popen" or
2627
name = "call" or
2728
name = "check_call" or
28-
name = "check_output"
29+
name = "check_output" or
30+
name = "run"
2931
)
3032
}
3133

@@ -75,9 +77,17 @@ class ShellCommand extends TaintSink {
7577
istrue.booleanValue() = true
7678
)
7779
or
80+
exists(CallNode call, string name |
81+
call.getAnArg() = this and
82+
call.getFunction().refersTo(osOrPopenModule().getAttribute(name)) |
83+
name = "system" or
84+
name = "popen" or
85+
name.matches("popen_")
86+
)
87+
or
7888
exists(CallNode call |
79-
call.getFunction().refersTo(osModule().getAttribute("system")) and
80-
call.getAnArg() = this
89+
call.getAnArg() = this and
90+
call.getFunction().refersTo(any(ModuleObject commands | commands.getName() = "commands"))
8191
)
8292
}
8393

python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ edges
1010
| command_injection.py:24:11:24:37 | externally controlled string | command_injection.py:25:23:25:25 | externally controlled string |
1111
| command_injection.py:25:23:25:25 | externally controlled string | command_injection.py:25:22:25:36 | first item in sequence of externally controlled string |
1212
| command_injection.py:25:23:25:25 | externally controlled string | command_injection.py:25:22:25:36 | sequence of externally controlled string |
13+
| command_injection.py:30:13:30:24 | dict of externally controlled string | command_injection.py:30:13:30:41 | externally controlled string |
14+
| command_injection.py:30:13:30:41 | externally controlled string | command_injection.py:32:22:32:26 | externally controlled string |
15+
| command_injection.py:32:14:32:26 | externally controlled string | ../lib/os/__init__.py:4:11:4:13 | externally controlled string |
16+
| command_injection.py:32:22:32:26 | externally controlled string | command_injection.py:32:14:32:26 | externally controlled string |
1317
parents
1418
| ../lib/os/__init__.py:1:12:1:14 | externally controlled string | command_injection.py:12:15:12:27 | externally controlled string |
19+
| ../lib/os/__init__.py:4:11:4:13 | externally controlled string | command_injection.py:32:14:32:26 | externally controlled string |
1520
#select
1621
| command_injection.py:12:15:12:27 | shell command | command_injection.py:10:13:10:24 | dict of externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | This command depends on $@. | command_injection.py:10:13:10:24 | flask.request.args | a user-provided value |
1722
| command_injection.py:19:22:19:34 | shell command | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | This command depends on $@. | command_injection.py:17:13:17:24 | flask.request.args | a user-provided value |
1823
| command_injection.py:25:22:25:36 | OS command first argument | command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:25:22:25:36 | first item in sequence of externally controlled string | This command depends on $@. | command_injection.py:24:11:24:22 | flask.request.args | a user-provided value |
24+
| command_injection.py:32:14:32:26 | shell command | command_injection.py:30:13:30:24 | dict of externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | This command depends on $@. | command_injection.py:30:13:30:24 | flask.request.args | a user-provided value |

python/ql/test/query-tests/Security/CWE-078/command_injection.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,10 @@ def command_injection2():
2323
def first_arg_injection():
2424
cmd = request.args.get('cmd', '')
2525
subprocess.Popen([cmd, "param1"])
26+
27+
28+
@app.route("/other_cases")
29+
def others():
30+
files = request.args.get('files', '')
31+
# Don't let files be `; rm -rf /`
32+
os.popen("ls " + files)
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
def system(cmd, *args, **kwargs):
2-
return None
2+
return None
3+
4+
def popen(cmd, *args, **kwargs):
5+
return None

0 commit comments

Comments
 (0)