← All cybersecurity posts

OS Command Injection: Examples and Prevention

OS command injection occurs when an application builds a shell command using user input. Shells interpret special characters like ;, &&, | and backticks, so an attacker can append their own commands.

Typical vulnerable code

Developers often reach for the shell to do something quickly: ping a host, convert a file, generate a thumbnail or call a CLI tool.

// Node.js, vulnerable
const { exec } = require("child_process");
exec(`ping -c 1 ${req.query.host}`, (err, out) => res.send(out));

With host=8.8.8.8; id, the server runs id as well.

// PHP, vulnerable
system("nslookup " . $_GET["domain"]);

Shell metacharacters attackers use

CharacterEffect
;Run another command
&& and double pipeRun conditionally
Single pipePipe output into another command
Backticks and $()Command substitution
NewlineStart a new command

Blind command injection

Often the output is not returned. Attackers then use:

  • Time delays, such as sleep 10, to confirm execution.
  • Out-of-band callbacks, such as a DNS lookup or HTTP request to a server they control.
  • Writing files to a web-accessible location.

How to prevent command injection

1. Avoid the shell entirely. Use a library for the task, such as an image processing library instead of calling ImageMagick through a shell.

2. If you must run a program, pass arguments as an array without a shell.

// Node.js, safe
const { execFile } = require("child_process");
execFile("ping", ["-c", "1", host], (err, out) => res.send(out));
# Python, safe
subprocess.run(["ping", "-c", "1", host], check=True)

3. Validate input strictly. For a hostname, allow only valid hostname characters. For an IP, parse it as an IP.

4. Watch for argument injection. Even without a shell, an input starting with - can be interpreted as an option. Use -- to end options where supported.

5. Run with least privilege so a successful injection has limited impact.

Where to look in your codebase

Search for exec, system, popen, shell_exec, passthru, spawn with shell: true, subprocess with shell=True, and backticks in Ruby or PHP. Review each call for user-influenced input.

Testing for command injection

SecurityInfinity agents test parameters for command injection using safe, time-based and out-of-band techniques, confirm execution, and point to the vulnerable call. Start free.

Frequently asked questions

Is escaping user input enough?

Escaping functions exist, but they are easy to misuse. Avoiding the shell and passing arguments as arrays is much safer.

What is the difference between command injection and code injection?

Command injection runs operating system commands. Code injection runs code in the application's own language, for example via eval().

How severe is command injection?

Usually critical, since it typically gives the attacker the ability to run arbitrary commands with the application's privileges.

Find what attackers can see, before they do.

AI pentesting agents test your web app, API and cloud, and every finding comes with evidence and a fix. First report in about 10 minutes.

Stay ahead of attackers.

New vulnerabilities, breach write-ups and practical fixes. A few emails a month.