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-69243-poc-aiohttp-smuggling — Riproduce il request smuggling aiohttp CWE-444 tramite upgrade WebSocket rifiutati, con payload Python/Rust e lab Docker che dimostra il bypass del controllo accessi del proxy. | Kitploit
Strumenti/GitHubGitHub/jvbotelho/cve-2026-69243-poc-aiohttp-smuggling
Generazione di PayloadAnalisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebSicurezza WebFuzzing
GitHubjvbotelho/cve-2026-69243-poc-aiohttp-smuggling

cve-2026-69243-poc-aiohttp-smuggling

Riproduce il request smuggling aiohttp CWE-444 tramite upgrade WebSocket rifiutati, con payload Python/Rust e lab Docker che dimostra il bypass del controllo accessi del proxy.

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 →
Vedi RepositorySito web
161 mese faNon ancora revisionato
Condividi

CVE-2026-69243 — aiohttp Request Smuggling (CWE-444)

Request smuggling tramite upgrade WebSocket rifiutato in aiohttp < 3.14.2. Quando un reverse proxy inoltra le intestazioni Connection: Upgrade + Upgrade: websocket, il parser aiohttp vulnerabile salta il corpo della richiesta e interpreta i byte finali come una richiesta in pipeline — bypassando i controlli di accesso perimetrali.

Corretto in aiohttp 3.14.2 (commit 6ae358f).

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

Esecuzione in 60 secondi

Copia e incolla la versione Python (zero dipendenze, 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>

Oppure compila la versione Rust (zero dipendenze, 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>

I binari precompilati verranno pubblicati nella sezione Releases tramite il workflow di release.

Entrambi producono payload byte-identici (garantito da un test di parità CI). Se il lab è in esecuzione:

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

Output atteso: 1 risposta HTTP (WebSocket upgrade rejected). Poi verifica:

root@kitploit:~
# Backend processed 2 requests (/ws + smuggled /admin):
docker logs backend-vuln | grep -c '"path".*"/admin"'

# Nginx only logged 1 request (the /ws):
docker exec nginx-upgrade cat /logs/nginx-upgrade.access.log | grep -c '/admin'

Un conteggio del backend > 0 e un conteggio di Nginx = 0 significano che lo split CWE-444 è confermato. Questo PoC invia un singolo segmento TCP — il corpo è la richiesta contrabbandata; Nginx lo tratta come corpo, aiohttp lo tratta come una seconda richiesta.

Cosa dimostra

  • Confusione del parser: aiohttp 3.14.1 _http_parser.pyx restituisce 2 (salta il corpo) al rilevamento dell'upgrade prima che il corpo sia consumato (riga ~863). I byte del corpo rimangono in _message_tail e vengono reimmessi nel parser in web_protocol.py finish_response (riga ~771).
  • Split CWE-444: Il frontend vede 1 richiesta con corpo; il backend vede 2 richieste in pipeline. Discrepanza nel conteggio delle richieste nei log.
  • Bypass del controllo di accesso: quando Nginx ha location /admin { deny all; }, la /admin contrabbandata raggiunge comunque il backend perché le decisioni di routing di Nginx vengono prese solo sulla richiesta esterna.
  • Nessuna correzione a livello di handler: await request.read() restituisce 0 byte sulle richieste di upgrade in 3.14.1 — il corpo è trattenuto al di sotto del livello handler. Applica la patch, oppure rimuovi le intestazioni di upgrade sul proxy per le route che non devono cambiare protocollo.

Lab 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

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

Servizi:

ContainerScopo
backend-vulnaiohttp 3.14.1 (vulnerabile), READ_BODY=false
backend-vuln-readaiohttp 3.14.1, READ_BODY=true (dimostra che l'handler non può aiutare)
backend-patchedaiohttp 3.14.2 (corretto)
nginx-upgradeInoltra le intestazioni di upgrade (deny all su /admin)
nginx-defaultNessun inoltro di upgrade (neutralizza il bug)
nginx-stripRimozione Connection "" (neutralizza il bug)
attackerBinari Rust: reproduce, fase2, poc

Risultati completi delle fasi 1-3 in findings/.

Limitazioni (oneste)

  • Il proxy deve inoltrare le intestazioni di upgrade. Le configurazioni Nginx che inviano Connection: close al backend o rimuovono le intestazioni Connection/Upgrade non sono vulnerabili. La configurazione che espone lo split è lo snippet map WebSocket canonico dalla documentazione di proxy di Nginx.
  • La risposta contrabbandata viene assorbita dal proxy. Nella catena Nginx dimostrata, l'attaccante riceve solo la risposta /ws esterna. L'evidenza del contrabbando è nei log del backend, non nella risposta dell'attaccante — una primitiva cieca e unidirezionale in questa topologia. Altre topologie di proxy non sono state testate.
  • Il framing chunked è di fatto specifico del CL. Direttamente contro aiohttp, Transfer-Encoding: chunked NON provoca contrabbando — la riga grezza della dimensione del chunk (es. 3e) arriva al parser come metodo non valido e la connessione muore. Attraverso Nginx funziona, ma tramite normalizzazione: Nginx de-chunka il corpo e inoltra un Content-Length sintetizzato, quindi il backend viene sfruttato attraverso lo stesso percorso CL. Il flag --chunked dimostra il percorso del proxy.
  • Richiede un endpoint che rifiuta gli upgrade WebSocket. L'handler deve restituire una risposta non-WebSocketResponse. La maggior parte delle app che non usano WebSocket su una route rifiutano per impostazione predefinita (il framework restituisce 404 o passa al prossimo handler).

File

root@kitploit:~
poc/                       # Rust cargo project
├── Cargo.toml
├── src/main.rs            # CLI binary
├── src/lib.rs             # Library + unit tests
├── tests/parity.rs        # Cross-language payload parity test
└── fuzz/                  # cargo-fuzz targets
poc.py                     # Python PoC (copy-paste from blog)
attacker/ backend/ frontend/   # Docker lab services
docker-compose.yml         # 7-service lab
findings/                  # Research notes (Phase 1-3)
.github/workflows/
├── ci.yml                 # Build, test, clippy, parity, integration, fuzz
└── release.yml            # Cross-compile + GitHub Release

Rilevamento

Vedi findings/fase3-deteccao.md per l'analisi completa. Riepilogo, con le avvertenze che contano in produzione:

  1. Discrepanza nel conteggio delle richieste (backend > frontend) sulla stessa connessione. Nota: il backend registra l'IP del proxy, non quello del client — la correlazione richiede la normalizzazione di X-Forwarded-For/Proxy Protocol, oppure il logging di un ID di connessione upstream + sequenza richieste per connessione. Solo IP client + finestra temporale è debole (NAT, keep-alive, concorrenza).
  2. Richiesta del backend senza corrispondenza nel frontend: un percorso ristretto nel log del backend senza voce corrispondente nel log di accesso del frontend. Filtra le sottoreti interne (gli health check che bypassano il proxy danno falsi positivi).
  3. Rifiuto dell'upgrade + percorso diverso dallo stesso client in ~100 ms. Confidenza media — il pipelining legittimo produce intervalli simili; il logger del lab non registra la porta remota/ID di connessione, quindi "nessuna nuova handshake TCP" NON è qualcosa che questi dati dimostrano.
  4. Middleware per il corpo trattenuto (content_length vs byte da request.read()): scatta su 3.14.1, silenzioso su 3.14.2. Rileva, non mitiga. Limitane l'ambito (route candidate per upgrade, corpi piccoli, stati non-101) — la versione ingenua bufferizza ogni corpo in memoria.
  5. reqlen di edge oltre la baseline delle intestazioni sugli endpoint WebSocket — confidenza bassa da solo (cookie/JWT/intestazioni di tracing sono rumorosi), ma l'unico segnale edge quando il client non invia Content-Length (ingresso chunked).
Scarica lo strumento