Cybreach Consulting Prendre RDV
Retour aux articles
Vulnérabilités

Injection de commandes OS et injection d'en-têtes HTTP

L'injection de commandes OS se produit quand une application construit une commande shell en y incluant des données contrôlées par l'utilisateur sans les valider ni les échapper. Le shell interprète les métacaractères (;, &, |, $(), `) comme des séparateurs de commandes. Un attaquant peut ainsi enchaîner ses propres commandes à celles prévues par l'application.

Exemple d'injection de commandes et correction

// 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

Mécanismes d'injection courants

Les fonctions à risque sont celles qui passent une chaîne au shell : exec(), system(), shell_exec(), popen() (PHP/Python), os.system(), subprocess.call(shell=True) (Python), Runtime.exec(String) (Java). Ces fonctions invoquent un shell intermédiaire (/bin/sh) qui interprète les métacaractères.

Python : subprocess sans 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')

Injection d'en-têtes HTTP

L'injection d'en-têtes HTTP se produit quand une valeur contrôlée par l'utilisateur est insérée dans un en-tête de réponse HTTP sans validation. Les en-têtes HTTP sont délimités par des retours à la ligne (\r\n). Un attaquant qui peut insérer ces caractères peut ajouter des en-têtes arbitraires ou même scinder la réponse HTTP pour injecter une réponse complète (HTTP response splitting).

En-tête de redirection vulnérable

# 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 = '/'

Règle générale

La règle de prévention est universelle : ne jamais construire une commande ou un en-tête par concaténation de chaînes incluant des entrées utilisateur. Pour les commandes système, toujours passer les arguments séparément (liste, pas une chaîne). Pour les en-têtes HTTP, valider et encoder les valeurs. La plupart des frameworks modernes gèrent l'encodage des en-têtes automatiquement si l'on utilise leurs APIs dédiées.

Une question après la lecture ?

Votre code invoque des commandes système avec des entrées utilisateur ? Envoyez un message.

Ou réservez directement un cadrage de 30 minutes

Réserver un appel