Interactive Playground
Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.
pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-010 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects OS command injection vulnerabilities in Django applications where untrusted user input from HTTP request parameters flows into os.system() calls.
os.system() executes its string argument through the system shell (typically /bin/sh on Unix). When user-controlled data is embedded in the command string, an attacker can inject shell metacharacters (semicolons, pipes, backticks, $() substitution) to execute arbitrary commands. The injected commands run with the full privileges of the Django application process, which on misconfigured servers may include file system access, network access, or even root privileges.
os.system() is particularly dangerous because it always invokes the shell, unlike subprocess.run() with a list argument which can bypass the shell entirely. There is no safe way to use os.system() with user-controlled input; the function should be replaced with subprocess.run() using a list of arguments.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Arbitrary Command Execution
An attacker who controls any part of the string passed to os.system() can execute arbitrary commands on the server. Shell metacharacters like semicolons, pipes, backticks, and $() let attackers chain additional commands after the intended one, regardless of what comes before or after their input in the string.
Full Server Compromise
Commands injected through os.system() run as the Django application user. In containerized environments this is typically a low-privilege user, but in misconfigured deployments it may be the web server user or even root. In either case, an attacker can read application secrets, modify files, install backdoors, or pivot to other internal network services.
Data Exfiltration via DNS or HTTP
Even in restricted environments, attackers can use injected commands to exfiltrate data through DNS lookups, outbound HTTP requests, or writing to files served by the web application itself. Command injection in a restricted container can still lead to credential theft and lateral movement.
Reverse Shell Establishment
Attackers commonly use command injection to establish a reverse shell connection back to attacker-controlled infrastructure, providing persistent interactive access to the server even after the original vulnerability is patched.
How to Fix
Recommended remediation steps
- 1Replace all os.system() calls with subprocess.run() using a list of arguments, which avoids shell interpretation entirely.
- 2Never use shell=True in subprocess calls when any argument originates from user input, as this reintroduces shell injection risk.
- 3Validate user input against strict allowlists (e.g., regex for hostname format, explicit set membership for command names) before passing to subprocess.
- 4Consider whether the functionality requiring shell commands can be achieved with Python standard library functions that don't invoke a shell at all.
- 5Run Django applications under a dedicated low-privilege user account and container security profiles (seccomp, AppArmor) to limit the impact of any successful injection.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule performs inter-procedural taint analysis with global scope. Sources include calls("request.GET.get"), calls("request.POST.get"), calls("request.GET.__getitem__"), calls("request.POST.__getitem__"), calls("request.body"), and calls("request.read"). The sink is calls("os.system") with the tainted value tracked via .tracks(0) (the command string argument). Sanitizers include shlex.quote() for shell-escaped strings and explicit allowlist membership checks. The analysis follows taint across file and module boundaries.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Python
Django Command Injection via subprocess
User input flows to subprocess with shell=True or as a string command, enabling OS command injection.
Django Code Injection via eval()
User input flows to eval(), enabling arbitrary Python code execution on the server.
Django Code Injection via exec()
User input flows to exec(), enabling arbitrary Python statement execution on the server.
Frequently Asked Questions
Common questions about Django Command Injection via os.system()
New feature
Get these findings posted directly on your GitHub pull requests
The Django Command Injection via os.system() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.