python.django.security.injection.command.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:
- patterns:
- pattern-inside: |
def $FUNC(..., $REQUEST, ...):
...
- focus-metavariable: $REQUEST
- metavariable-pattern:
metavariable: $REQUEST
patterns:
- pattern: request
- pattern-not-inside: request.build_absolute_uri
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: MEDIUM
impact: HIGH
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
Examples
subprocess-injection.py
import json
import subprocess
import sys
import django
def a(request):
ip = request.POST.get("ip")
# ruleid:subprocess-injection
subprocess.run("ping "+ ip)
def b(request):
host = request.headers["HOST"]
# ruleid:subprocess-injection
subprocess.run("echo {} > log".format(host))
def d(request):
cmd = request.POST.get("cmd")
ip = request.POST.get("ip")
command = [cmd, ip]
# ruleid:subprocess-injection
subprocess.capture_output(command)
def e(request):
event = json.loads(request.body)
cmd = event['id'].split()
# ruleid:subprocess-injection
subprocess.call([cmd[0], cmd[1], "some", "args"])
def f(request):
event = json.loads(request.body)
# ruleid:subprocess-injection
subprocess.run(["bash", "-c", event['id']], shell=True)
def g(request):
event = request.body
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)
def d_ok(request):
# ok:subprocess-injection
cmd = request.POST.get("cmd")
ip = request.POST.get("ip")
subprocess.capture_output(["ping", cmd, ip])
def d_ok2(request):
ip = request.POST.get("ip")
cmd = ["ping", ip]
# ok:subprocess-injection
subprocess.capture_output(cmd)
def e_ok(request):
allowed = {'p': "ping"}
event = json.loads(request.body)
cmd = event['id'].split()
valid = allowed[cmd[0]]
# ok:subprocess-injection
subprocess.call([valid, "some", "args"])
def ok(request):
ip = request.POST.get("ip")
# ok:subprocess-injection
subprocess.run(["ping", ip])
def ok2(request):
ip = request.POST.get("ip")
# ok:subprocess-injection
subprocess.run("echo 'nothing'")
def ok3(request):
ip = request.POST.get("ip")
# ok:subprocess-injection
subprocess.call(["echo", "a", ";", "rm", "-rf", "/"])
Short Link: https://sg.run/49BE