Cybreach Consulting Prendre RDV
Back to articles
Vulnerabilities

OS command injection and HTTP header injection

OS command injection occurs when an application builds a shell command including user-controlled data without validating or escaping it. The shell interprets metacharacters (;, &, |, $(), `) as command separators. An attacker can thus chain their own commands to those intended by the application.

Command injection example and fix

// Vulnérable : concaténation directe dans une commande shell
const { exec } = require('child_process');
exec(`ping -c 1 ${req.query.host}`, callback);

// Payload : req.query.host = "8.8.8.8; cat /etc/passwd"
// Résultat : ping + lecture de /etc/passwd

// Corrigé : passage des arguments séparément, sans shell
const { execFile } = require('child_process');
execFile('ping', ['-c', '1', req.query.host], callback);
// execFile n'invoque pas de shell : les métacaractères sont ignorés

Common injection mechanisms

At-risk functions are those that pass a string to the shell: exec(), system(), shell_exec(), popen() (PHP/Python), os.system(), subprocess.call(shell=True) (Python), Runtime.exec(String) (Java). These functions invoke an intermediate shell (/bin/sh) that interprets metacharacters.

Python: subprocess without shell

import subprocess

# Vulnérable
subprocess.call(f"convert {filename} output.jpg", shell=True)

# Corrigé : liste d'arguments, shell=False (défaut)
subprocess.call(['convert', filename, 'output.jpg'])

# Ou avec validation stricte de l'entrée :
import re
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
    raise ValueError('Invalid filename')

HTTP header injection

HTTP header injection occurs when a user-controlled value is inserted into an HTTP response header without validation. HTTP headers are delimited by carriage returns (\r\n). An attacker who can insert these characters can add arbitrary headers or even split the HTTP response to inject a complete response (HTTP response splitting).

Vulnerable redirect header

# Vulnérable : la valeur de redirect est insérée dans l'en-tête Location
redirect_url = request.args.get('redirect')
response.headers['Location'] = redirect_url

# Payload : redirect = "http://legit.com\r\nSet-Cookie: session=attacker"
# Résultat : injection d'un cookie Set-Cookie dans la réponse

# Corrigé : validation stricte de l'URL
from urllib.parse import urlparse
parsed = urlparse(redirect_url)
if parsed.netloc not in ALLOWED_DOMAINS:
    redirect_url = '/'

General rule

The prevention rule is universal: never build a command or header by string concatenation including user input. For system commands, always pass arguments separately (list, not a string). For HTTP headers, validate and encode values. Most modern frameworks handle header encoding automatically when using their dedicated APIs.

A question after reading?

Does your code invoke system commands with user input? Send a message.

Or book a 30-minute scoping call directly

Book a call