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-LANG-SEC-010 --project .About This Rule
Understanding the vulnerability and how it is detected
Python's os.system() and os.popen() functions execute a shell command string by invoking the system shell (/bin/sh on Unix, cmd.exe on Windows). Because the command is passed through a shell interpreter, special characters such as semicolons, pipes, backticks, dollar signs, and redirections are interpreted as shell metacharacters.
When any part of the command string is derived from untrusted user input, an attacker can inject additional commands using shell metacharacters. For example, user input of "file.txt; rm -rf /" would cause os.system("cat file.txt; rm -rf /") to execute both commands.
The recommended replacement is subprocess.run() with a list of arguments (not a shell string), which bypasses the shell entirely and passes arguments directly to the OS exec() syscall. subprocess.run(["cat", filename]) is safe regardless of what filename contains.
Security Implications
Potential attack scenarios if this vulnerability is exploited
OS Command Injection
Shell metacharacters in user-supplied input allow injecting additional commands. An attacker can chain arbitrary commands using semicolons, pipes, or newlines, executing them with the privileges of the Python process.
Data Exfiltration
Injected commands can read sensitive files (credentials, private keys, configuration), encode them, and send them to an attacker-controlled endpoint using curl, wget, or other available tools on the system.
Persistent Backdoor Installation
Command injection can write cron jobs, SSH authorized_keys, or systemd unit files that survive process restarts and provide persistent access to the attacker even after the vulnerability is patched.
Lateral Movement
On cloud environments or internal networks, injected commands can access metadata services for IAM credential theft, scan internal networks, and pivot to other systems using the compromised host's network access.
How to Fix
Recommended remediation steps
- 1Replace os.system() and os.popen() with subprocess.run() using a list of arguments, which bypasses the shell entirely.
- 2Never construct shell command strings by concatenating or interpolating user input, even if the input appears to be validated.
- 3If a shell pipeline is genuinely required, validate all user-supplied values against a strict allowlist before including them.
- 4Set a timeout on subprocess.run() to prevent resource exhaustion from long-running injected commands.
- 5Run the Python process with the minimum OS privileges required so that injected commands cannot escalate to root or access sensitive paths.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule detects calls to os.system(), os.popen(), os.popen2(), os.popen3(), and os.popen4() from the Python os module. All call sites are flagged since these functions always invoke the system shell and are vulnerable to injection whenever the command string includes non-literal content.
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
Dangerous os.exec*() Call
os.exec*() replaces the current process image with a new program, enabling arbitrary program execution when arguments are untrusted.
Dangerous os.spawn*() Call
os.spawn*() spawns a new process and can execute arbitrary programs when the executable path or arguments are derived from untrusted input.
Shell Command with Wildcard Character
os.system() calls containing wildcard characters (*) may lead to unintended file inclusion or command injection through wildcard expansion.
Frequently Asked Questions
Common questions about Dangerous os.system() or os.popen() Call
New feature
Get these findings posted directly on your GitHub pull requests
The Dangerous os.system() or os.popen() Call rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.