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-69243-poc-aiohttp-smuggling — Reproduce el contrabando de solicitudes CWE-444 de aiohttp mediante actualizaciones WebSocket rechazadas, con cargas útiles en Python/Rust y un laboratorio Docker que demuestra la omisión del control de acceso del proxy. | Kitploit
Herramientas/GitHubGitHub/jvbotelho/cve-2026-69243-poc-aiohttp-smuggling
Generación de PayloadsAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebFuzzing
GitHubjvbotelho/cve-2026-69243-poc-aiohttp-smuggling

cve-2026-69243-poc-aiohttp-smuggling

Reproduce el contrabando de solicitudes CWE-444 de aiohttp mediante actualizaciones WebSocket rechazadas, con cargas útiles en Python/Rust y un laboratorio Docker que demuestra la omisión del control de acceso del proxy.

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 RepositorioSitio web
1hace 16 díasAún no revisado

CVE-2026-69243 — Contrabando de solicitudes en aiohttp (CWE-444)

Contrabando de solicitudes mediante una actualización WebSocket rechazada en aiohttp < 3.14.2. Cuando un proxy inverso reenvía las cabeceras Connection: Upgrade + Upgrade: websocket, el analizador vulnerable de aiohttp omite el cuerpo de la solicitud e interpreta los bytes finales como una solicitud canalizada (pipelining) — omitiendo los controles de acceso perimetrales.

Corregido en aiohttp 3.14.2 (commit 6ae358f).

Autor: João Victor Botelho (JV Botelho) — https://glitchedcat.com

Ejecución en 60 segundos

Copia y pega la versión en Python (cero dependencias, solo stdlib):

root@kitploit:~
curl -O https://raw.githubusercontent.com/JVBotelho/cve-2026-69243-poc-aiohttp-smuggling/main/poc.py
python3 poc.py <proxy-host> <proxy-port> <backend-host>

O compila la versión en Rust (cero dependencias, solo std):

root@kitploit:~
git clone https://github.com/JVBotelho/cve-2026-69243-poc-aiohttp-smuggling
cd cve-2026-69243-poc-aiohttp-smuggling/poc
cargo build --release
./target/release/cve-2026-69243-poc <proxy-host> <proxy-port> <backend-host>

Los binarios precompilados se publicarán en Releases mediante el flujo de trabajo de releases.

Ambos generan payloads byte-idénticos (garantizado por una prueba de paridad en CI). Si el laboratorio está en ejecución:

root@kitploit:~
python3 poc.py nginx-upgrade 80 backend-vuln

Salida esperada: 1 respuesta HTTP (WebSocket upgrade rejected). Luego verifica:

root@kitploit:~
# El backend procesó 2 solicitudes (/ws + /admin contrabandeado):
docker logs backend-vuln | grep -c '"path".*"/admin"'

# Nginx solo registró 1 solicitud (la /ws):
docker exec nginx-upgrade cat /logs/nginx-upgrade.access.log | grep -c '/admin'

Si el recuento del backend > 0 y el recuento de Nginx = 0, la división CWE-444 está confirmada. Este PoC envía un único segmento TCP — el cuerpo es la solicitud contrabandeada; Nginx lo trata como cuerpo, aiohttp lo trata como una segunda solicitud.

Qué demuestra esto

  • Confusión del analizador: _http_parser.pyx de aiohttp 3.14.1 devuelve 2 (omitir cuerpo) al detectar la actualización antes de que se consuma el cuerpo (línea ~863). Los bytes del cuerpo permanecen en _message_tail y se devuelven al analizador en finish_response de web_protocol.py (línea ~771).
  • División CWE-444: El frontend ve 1 solicitud con cuerpo; el backend ve 2 solicitudes canalizadas. Discrepancia en el recuento de solicitudes en los registros.
  • Omisión del control de acceso: Cuando Nginx tiene location /admin { deny all; }, el /admin contrabandeado aún llega al backend porque las decisiones de enrutamiento de Nginx se toman solo sobre la solicitud externa.
  • Sin corrección a nivel de handler: await request.read() devuelve 0 bytes en solicitudes de actualización en 3.14.1 — el cuerpo se retiene por debajo de la capa del handler. Aplica el parche, o elimina las cabeceras de actualización en el proxy en rutas que no deban cambiar de protocolo.

Laboratorio completo

root@kitploit:~
git clone https://github.com/JVBotelho/cve-2026-69243-poc-aiohttp-smuggling
cd cve-2026-69243-poc-aiohttp-smuggling
docker compose up -d

# Ejecuta el PoC:
docker compose run --rm --entrypoint /app/poc attacker nginx-upgrade 80 backend-vuln

Servicios:

Hallazgos completos de las Fases 1-3 en findings/.

Limitaciones (honestas)

  • El proxy debe reenviar las cabeceras de actualización. Las configuraciones de Nginx que envían Connection: close al backend o eliminan las cabeceras Connection/Upgrade no son vulnerables. La configuración que expone la división es el fragmento canónico map para WebSocket de la propia documentación de proxy de Nginx.
  • La respuesta contrabandeada es absorbida por el proxy. En la cadena Nginx demostrada, el atacante solo recibe la respuesta externa /ws. La evidencia del contrabando está en los registros del backend, no en la respuesta del atacante — una primitiva ciega y unidireccional en esta topología. No se probaron otras topologías de proxy.
  • El framing por chunked es efectivo solo como CL. Directamente contra aiohttp, Transfer-Encoding: chunked NO genera contrabando — la línea cruda del tamaño de chunk (p. ej. 3e) llega al analizador como un método inválido y la conexión muere. A través de Nginx funciona, pero mediante normalización: Nginx elimina el chunked del cuerpo y reenvía un Content-Length sintetizado, por lo que el backend se explota a través de la misma vía CL. La bandera --chunked demuestra la vía del proxy.
  • Requiere un endpoint que rechace actualizaciones WebSocket. El handler debe devolver un que no sea válido. La mayoría de las aplicaciones que no usan WebSockets en una ruta lo rechazarán por defecto (el framework devuelve 404 o pasa al siguiente handler).

Archivos

root@kitploit:~
poc/                       # Proyecto cargo en Rust
├── Cargo.toml
├── src/main.rs            # Binario CLI
├── src/lib.rs             # Librería + pruebas unitarias
├── tests/parity.rs        # Prueba de paridad de payloads entre lenguajes
└── fuzz/                  # Objetivos cargo-fuzz
poc.py                     # PoC en Python (copiar y pegar desde el blog)
attacker/ backend/ frontend/   # Servicios del laboratorio Docker
docker-compose.yml         # Laboratorio de 7 servicios
findings/                  # Notas de investigación (Fases 1-3)
.github/workflows/
├── ci.yml                 # Compilar, probar, clippy, paridad, integración, fuzz
└── release.yml            # Compilación cruzada + GitHub Release

Detección

Consulta findings/fase3-deteccao.md para el análisis completo. Resumen, con las advertencias que importan en producción:

  1. Discrepancia en el recuento de solicitudes (backend > frontend) en la misma conexión. Ten en cuenta que el backend registra la IP del proxy, no la del cliente — la correlación requiere normalizar X-Forwarded-For/Proxy Protocol, o registrar un ID de conexión ascendente + la secuencia de solicitudes por conexión. La IP del cliente + la ventana de tiempo por sí solas son débiles (NAT, keep-alive, concurrencia).
  2. Solicitud en el backend sin correspondencia en el frontend: una ruta restringida en el registro del backend sin entrada correspondiente en el registro de acceso del frontend. Filtra subredes internas (los health checks que omiten el proxy generan falsos positivos).
  3. Rechazo de actualización + ruta diferente desde el mismo cliente en ~100 ms. Confianza media — el pipelining legítimo produce intervalos similares; el registrador del laboratorio no registra el puerto remoto/ID de conexión, por lo que "sin nuevo handshake TCP" NO es algo que estos datos demuestren.
  4. Middleware de cuerpo retenido (content_length vs bytes de request.read()): se activa en 3.14.1, silencioso en 3.14.2. Detecta, no mitiga. Acótalo (rutas candidatas a upgrade, cuerpos pequeños, estados no-101) — la versión ingenua almacena cada cuerpo en memoria.
  5. reqlen en el edge por encima de la línea base de cabeceras en endpoints WebSocket — confianza baja por sí sola (cookies/JWT/cabeceras de tracing generan ruido), pero es la única señal en el edge cuando el cliente no envía Content-Length (ingreso chunked).
Descargar herramienta
ContenedorPropósito
backend-vulnaiohttp 3.14.1 (vulnerable), READ_BODY=false
backend-vuln-readaiohttp 3.14.1, READ_BODY=true (demuestra que el handler no puede ayudar)
backend-patchedaiohttp 3.14.2 (corregido)
nginx-upgradeReenvía cabeceras de actualización (deny all en /admin)
nginx-defaultSin reenvío de actualización (neutraliza el bug)
nginx-stripEliminación Connection "" (neutraliza el bug)
attackerBinarios Rust: reproduce, fase2, poc
WebSocketResponse