Path traversal (CWE-22) : séquences ../ pour sortir du répertoire prévu et lire des fichiers sensibles, écrire du contenu arbitraire ou exécuter du code.
TL;DR
../ de base, encodée, null byte, injection de chemin absolu, Windows ..\\realpath après join, puis startsWith(base + sep) — les filtres par liste noire échouent toujoursLe path traversal (CWE-22) est une vulnérabilité dans laquelle une application construit un chemin du système de fichiers à partir d'une entrée contrôlée par l'utilisateur sans contraindre adéquatement l'endroit où ce chemin peut se résoudre. L'attaquant insère des séquences ../ — ou leurs équivalents encodés — pour naviguer vers le haut au-delà du répertoire de base prévu, atteignant des fichiers que l'application n'avait jamais prévu d'exposer. Sous OWASP A01:2021 (Broken Access Control), cela représente un échec à appliquer le principe de moindre privilège au niveau du système de fichiers.
La vulnérabilité apparaît partout où le code côté serveur construit un chemin à partir d'entrées externes : endpoints de téléchargement de fichiers, chargeurs de templates, serveurs d'images, visualiseurs de logs, générateurs de documents et installateurs de plugins. Une augmentation de 85 % d'une année sur l'autre de la prévalence dans les projets à code fermé (de 1,9 % à 3,5 % de tous les findings) issue de l'analyse Aikido 2024 montre que la surface d'attaque est en croissance. Plusieurs entrées CISA KEV en 2024 confirment une exploitation active à grande échelle.
Le path traversal se distingue du SSRF, qui manipule la résolution d'URL pour atteindre des services réseau internes. Les deux primitives se combinent parfois : urllib.parse.urljoin(base, "/etc/passwd") ignore silencieusement la base quand l'entrée utilisateur commence par /, créant une primitive de path traversal dans du code de récupération d'URL (CVE-2026-32871, FastMCP, CVSS 9.8).
La vulnérabilité a une seule cause racine structurelle : une application concatène un répertoire de base fixe avec une entrée utilisateur et passe le résultat à une fonction du système de fichiers sans d'abord vérifier que le chemin canonique résolu reste dans la base.
Cinq étapes d'exploitation :
file=, path=, template=, page=, include=, doc=, resource=.../../etc/passwd)..., s'échappant du répertoire prévu.Les marqueurs de confirmation diffèrent selon la cible. /etc/passwd retourne des lignes correspondant à root:x:0:0:. /proc/self/environ expose les variables d'environnement d'exécution qui contiennent fréquemment des valeurs AWS_SECRET_ACCESS_KEY, DATABASE_URL et API_KEY. WEB-INF/web.xml retourne du XML contenant <web-app.
| Variante | Technique | CVE clés | Impact |
|---|---|---|---|
Traversée ../ de base | Chaînes dot-dot-slash littérales | CVE-2024-23897, CVE-2023-2825 | Lecture de fichier arbitraire, LFI vers RCE |
| Traversée encodée | Encodage URL, double, Unicode trop long | CVE-2024-38819, CVE-2024-38474 | Contournement de filtres et WAF |
| Injection null byte | %00 termine le suffixe d'extension au niveau C | PHP pre-5.3.4, Java hérité | Contournement du filtre d'extension |
| Injection de chemin absolu | /etc/passwd direct contourne les filtres ../ | CVE-2026-32871, CVE-2023-50164 | Écrasement Python os.path.join |
| Path traversal Windows | ..\, %5c, UNC, ADS, noms 8.3 | CVE-2024-4577, CVE-2024-50379 | Lecture fichier IIS, webshell via TOCTOU |
Trois chaînes d'escalade convertissent systématiquement les lectures par path traversal en exécution de code à distance :
Empoisonnement de logs : injecter du code PHP dans les logs d'accès du serveur via une requête User-Agent: <?php system($_GET['cmd']); ?> forgée. Quand le fichier log est ensuite inclus via la LFI, l'interpréteur PHP exécute le payload injecté. Logs cibles : /var/log/apache2/access.log, /var/log/nginx/access.log, /var/log/auth.log, /proc/self/fd/2.
Chaînes de filtres PHP : php://filter/convert.base64-encode/resource=index.php retourne le code source PHP encodé en base64 sans nécessiter aucun accès en écriture. Les techniques avancées de chaînes de filtres (php_filter_chain_generator de Charles Fol) construisent des payloads PHP arbitraires ciblant /dev/null, réalisant du RCE non authentifié depuis une LFI sans upload de fichier requis.
ZIP slip : les archives malveillantes contiennent des entrées avec des séquences de traversée dans les noms de fichiers (../../var/www/html/shell.php). Une extraction naïve écrit le webshell dans le document root. CVE-2024-1708 (ConnectWise ScreenConnect, CVSS 8.4) chaîné avec le contournement d'authentification CVE-2024-1709 (CVSS 10.0) pour réaliser du RCE non authentifié en tant que SYSTEM dans les 24 heures suivant la divulgation.
CVE-2024-23897 — Jenkins CLI Parser (CVSS 9.8, CISA KEV) : la fonctionnalité expandAtFiles() de la bibliothèque args4j remplaçait les tokens @/path/to/file dans les commandes CLI par le contenu du fichier. Des attaquants non authentifiés lisaient /var/jenkins_home/secrets/master.key et /etc/passwd via HTTP, WebSocket ou SSH. Exploité par le groupe RansomEXX pour compromettre un fournisseur de la National Payments Corporation of India, causant des perturbations de service dans plus de 300 banques. Date limite de correction CISA obligatoire : 9 septembre 2024.
CVE-2024-1708 — ConnectWise ScreenConnect ZIP Slip (CVSS 8.4) : combiné avec le contournement d'authentification CVE-2024-1709 (CVSS 10.0), cela permettait à des attaquants non authentifiés d'écrire des webshells ASPX arbitraires dans le webroot de ScreenConnect. Plusieurs groupes de ransomware ont weaponisé la chaîne dans les 24 heures suivant la divulgation, réalisant un accès SYSTEM.
CVE-2023-2825 — GitLab Path Traversal non authentifié (CVSS 10.0) : lecture de fichier arbitraire via path traversal dans la gestion des URLs de projets imbriqués publics de GitLab CE/EE. Aucune authentification requise. Exposait /etc/passwd, clés privées SSH et tokens d'enregistrement de runner GitLab.
file=, path=, page=, template=, include=, doc=, resource=, lang=, layout=, theme=, download=.../../etc/passwd (Linux) ou ..\..\Windows\win.ini (Windows) et examiner le corps de la réponse pour des marqueurs de confirmation...%2f..%2fetc%2fpasswd, %2e%2e%2f%2e%2e%2fetc%2fpasswd, ..%252f..%252fetc%252fpasswd./etc/passwd directement.../../etc/passwd%00.txt — si la réponse diffère, un contournement par null byte est présent.../WEB-INF/web.xml — le marqueur est <web-app.root:x:0:0: (passwd), [fonts] (win.ini), <web-app (web.xml).BreachVex détecte le path traversal via plusieurs techniques complémentaires : analyse par templates lfi/traversal, fuzzing de paramètres sur un ensemble hiérarchisé de noms de paramètres sujets aux LFI, vérification HTTP directe avec 22 variantes de payload contre des marqueurs de contenu spécifiques à l'OS, et un prouveur LFI étendu avec une porte de contenu à trois critères (HTTP 200 + longueur du corps ≥ 10 octets + correspondance regex du marqueur). Les hits de fuzzing sans confirmation par la porte de contenu sont rétrogradés en findings potentiels de faible sévérité pour supprimer les faux positifs.
import os
BASE_DIR = os.path.realpath("/var/www/app/uploads")
def safe_open(filename: str):
# realpath résout tous les .., symlinks et percent-encoding
requested = os.path.realpath(os.path.join(BASE_DIR, filename))
# Le sep final empêche /uploads_backup de passer la vérification /uploads
if not requested.startswith(BASE_DIR + os.sep):
raise ValueError("Tentative de path traversal bloquée")
return open(requested, "rb")// Java — getCanonicalFile résout .. et les symlinks
File base = new File("/app/uploads").getCanonicalFile();
File requested = new File(base, userInput).getCanonicalFile();
if (!requested.toPath().startsWith(base.toPath())) {
throw new SecurityException("Tentative de path traversal bloquée");
}La défense la plus robuste élimine entièrement les noms de fichiers contrôlés par l'utilisateur. Mapper des identifiants opaques vers de vrais chemins de fichiers dans le code côté serveur :
DOCUMENT_MAP = {
"q1_report": "/app/reports/2025_q1.pdf",
"q2_report": "/app/reports/2025_q2.pdf",
}
def get_document(key: str):
path = DOCUMENT_MAP.get(key)
if path is None:
raise ValueError("Clé de document inconnue")
return open(path, "rb")Ne jamais implémenter la défense contre le path traversal en utilisant des filtres par liste noire. Chaque encodage connu de ../ a des contournements documentés : ....// défait les filtres de suppression en une seule passe, %252e%252e%252f défait les filtres de décodage unique, et ..%c0%af exploite les décodeurs UTF-8 trop longs. La seule approche correcte est la résolution canonique de chemin suivie d'une assertion de préfixe de base.
Déployer des chroot jails, des contrôles d'accès mandatoires AppArmor/SELinux ou des systèmes de fichiers de conteneur en lecture seule pour limiter le rayon de blast. Même en cas d'échec de la validation de chemin, un processus correctement confiné ne peut pas accéder aux fichiers en dehors de sa vue restreinte du système de fichiers.
../ classiques, cibles de fichiers Linux, et la chaîne d'empoisonnement de logs LFI-vers-RCE./etc/passwd directs et comportement Python os.path.join.%00 en PHP, Java et Go...\, chemins UNC, Alternate Data Streams et CVE-2024-4577.file:// ou les particularités de urljoin.Le path traversal (CWE-22) est une vulnérabilité dans laquelle une application construit un chemin du système de fichiers à partir d'une entrée contrôlée par l'utilisateur sans validation appropriée. Les attaquants insèrent des séquences comme ../ pour échapper au répertoire de base prévu et accéder à des fichiers arbitraires sur le serveur, y compris des fichiers de configuration, des clés privées et des credentials.
Le path traversal est la classe plus large : l'attaquant lit ou écrit des fichiers en dehors d'un répertoire prévu. Le Local File Inclusion (LFI) est une variante propre à PHP où la traversée atteint include() ou require(), faisant exécuter le fichier comme code PHP plutôt que d'être simplement lu. Toute LFI est un path traversal, mais tout path traversal n'est pas une LFI.
Le path traversal est mappé à OWASP A01:2021 — Broken Access Control. CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) est l'identifiant de faiblesse canonique utilisé dans les descriptions CVE.
Le path traversal représente 2,75 % de toutes les vulnérabilités open source et 3,5 % des findings en code fermé — une augmentation de 85 % de la prévalence dans les projets à code fermé selon la recherche Aikido. Plusieurs CVE avec CVSS 9.8+ ont été activement exploités en 2024, notamment CVE-2024-23897 (Jenkins, CISA KEV) et CVE-2024-1708 (ConnectWise ScreenConnect).
Sous Linux : /etc/passwd, /etc/shadow, /proc/self/environ (variables d'environnement avec des secrets), ~/.ssh/id_rsa, ~/.aws/credentials, et les fichiers .env de l'application. Sous Windows : C:\Windows\win.ini, C:\inetpub\wwwroot\web.config, et les fichiers de sauvegarde SAM. En Java : WEB-INF/web.xml et application.properties.
Oui, via plusieurs chaînes : LFI plus empoisonnement de logs injecte du code PHP dans les logs d'accès Apache/Nginx via User-Agent puis inclut le log ; les chaînes de filtres PHP génèrent des payloads PHP arbitraires sans upload de fichier ; le ZIP slip extrait un webshell en dehors du répertoire prévu. CVE-2024-1708 (ScreenConnect) a réalisé un accès SYSTEM non authentifié via ZIP slip.
Les filtres qui bloquent le `../` littéral ratent souvent les variantes encodées. La séquence ../ peut s'exprimer comme %2e%2e%2f (encodage URL), %252e%252e%252f (double encodage), ..%c0%af (UTF-8 trop long), ou ....// (séquence imbriquée qui devient ../ après une suppression en une seule passe). Chaque cycle de décodage dans le pipeline de traitement est une opportunité potentielle de contournement.
Résoudre le chemin canonique après avoir joint l'entrée utilisateur avec le répertoire de base, puis affirmer qu'il commence toujours par la base plus le séparateur de chemin. En Python : os.path.realpath(os.path.join(BASE, user_input)), puis vérifier startswith(BASE + os.sep). En Java : new File(base, input).getCanonicalFile(), puis vérifier toPath().startsWith(base.toPath()). Les filtres par liste noire échouent toujours.