python.flask.security.injection.subprocess-injection.subprocess-injection

Author
unknown
Download Count*
License
Detected user input entering a subprocess
call unsafely. This could result in a command injection vulnerability. An attacker could use this vulnerability to execute arbitrary commands on the host, which allows them to download malware, scan sensitive data, or run any command they wish on the server. Do not let users choose the command to run. In general, prefer to use Python API versions of system commands. If you must use subprocess, use a dictionary to allowlist a set of commands.
Run Locally
Run in CI
Defintion
rules:
- id: subprocess-injection
languages:
- python
mode: taint
options:
symbolic_propagation: true
pattern-sources:
- pattern-either:
- patterns:
- pattern-either:
- pattern: flask.request.form.get(...)
- pattern: flask.request.form[...]
- pattern: flask.request.args.get(...)
- pattern: flask.request.args[...]
- pattern: flask.request.values.get(...)
- pattern: flask.request.values[...]
- pattern: flask.request.cookies.get(...)
- pattern: flask.request.cookies[...]
- pattern: flask.request.stream
- pattern: flask.request.headers.get(...)
- pattern: flask.request.headers[...]
- pattern: flask.request.data
- pattern: flask.request.full_path
- pattern: flask.request.url
- pattern: flask.request.json
- pattern: flask.request.get_json()
- pattern: flask.request.view_args.get(...)
- pattern: flask.request.view_args[...]
- patterns:
- pattern: |
@$APP.route($ROUTE, ...)
def $FUNC(..., $ROUTEVAR, ...):
...
- focus-metavariable: $ROUTEVAR
pattern-sinks:
- patterns:
- pattern-either:
- patterns:
- pattern: subprocess.$FUNC(...)
- pattern-not: subprocess.$FUNC("...", ...)
- pattern-not: subprocess.$FUNC(["...", ...], ...)
- pattern-not-inside: |
$CMD = ["...", ...]
...
subprocess.$FUNC($CMD, ...)
- patterns:
- pattern: subprocess.$FUNC(["$SHELL", "-c", ...], ...)
- metavariable-regex:
metavariable: $SHELL
regex: ^(sh|bash|ksh|csh|tcsh|zsh)$
- patterns:
- pattern: subprocess.$FUNC(["$INTERPRETER", ...], ...)
- metavariable-regex:
metavariable: $INTERPRETER
regex: ^(python|python\d)$
pattern-sanitizers:
- patterns:
- pattern: $DICT[$KEY]
- focus-metavariable: $KEY
severity: ERROR
message: Detected user input entering a `subprocess` call unsafely. This could
result in a command injection vulnerability. An attacker could use this
vulnerability to execute arbitrary commands on the host, which allows them
to download malware, scan sensitive data, or run any command they wish on
the server. Do not let users choose the command to run. In general, prefer
to use Python API versions of system commands. If you must use subprocess,
use a dictionary to allowlist a set of commands.
metadata:
category: security
technology:
- flask
owasp:
- A01:2017 - Injection
- A03:2021 - Injection
cwe:
- "CWE-78: Improper Neutralization of Special Elements used in an OS
Command ('OS Command Injection')"
references:
- https://semgrep.dev/docs/cheat-sheets/python-command-injection/
confidence: HIGH
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- vuln
likelihood: HIGH
impact: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
Examples
subprocess-injection.py
import subprocess
import sys
import flask
app = flask.Flask(__name__)
@app.route("a")
def a():
ip = flask.request.args.get("ip")
# ruleid:subprocess-injection
subprocess.run("ping "+ ip)
@app.route("b")
def b():
host = flask.request.headers["HOST"]
# ruleid:subprocess-injection
subprocess.run("echo {} > log".format(host))
@app.route("c/<ip>")
def c(ip):
# ruleid:subprocess-injection
subprocess.run("ping "+ ip)
@app.route("d/<cmd>/<ip>")
def d(cmd, ip):
command = [cmd, ip]
# ruleid:subprocess-injection
subprocess.capture_output(command)
@app.route("e")
def e():
event = flask.request.json
cmd = event['id'].split()
# ruleid:subprocess-injection
subprocess.call([cmd[0], cmd[1], "some", "args"])
@app.route("f")
def f():
event = flask.request.get_json()
# ruleid:subprocess-injection
subprocess.run(["bash", "-c", event['id']], shell=True)
@app.route("g")
def g():
event = flask.request.json
python_file = f"""
print("What is your name?")
name = input()
print("Hello " + {event['id']})
"""
# ruleid:subprocess-injection
program = subprocess.Popen(['python2', python_file], stdin=subprocess.PIPE, text=True)
program.communicate(input=payload, timeout=1)
@app.route("d_ok/<cmd>/<ip>")
def d_ok(cmd, ip):
# ok:subprocess-injection
subprocess.capture_output(["ping", cmd, ip])
@app.route("d_ok2/<ip>")
def d_ok2(ip):
cmd = ["ping", ip]
# ok:subprocess-injection
subprocess.capture_output(cmd)
@app.route("e")
def e_ok():
allowed = {'p': "ping"}
event = flask.request.json
cmd = event['id'].split()
valid = allowed[cmd[0]]
# ok:subprocess-injection
subprocess.call([valid, "some", "args"])
@app.route("ok")
def ok():
ip = flask.request.args.get("ip")
subprocess.run(["ping", ip])
@app.route("ok2")
def ok2():
ip = flask.request.args.get("ip")
subprocess.run("echo 'nothing'")
@app.route("ok3")
def ok3():
ip = flask.request.args.get("ip")
subprocess.call(["echo", "a", ";", "rm", "-rf", "/"])
Short Link: https://sg.run/5gW3