Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
cve-2026-69243-poc-aiohttp-smuggling — Reproduz contrabando de requisições (CWE-444) em aiohttp por meio de upgrades WebSocket rejeitados, com payloads em Python/Rust e laboratório Docker demonstrando bypass do controle de acesso de proxy. | Kitploit
Ferramentas/GitHubGitHub/jvbotelho/cve-2026-69243-poc-aiohttp-smuggling
Geração de PayloadsAnálise de VulnerabilidadesExploraçãoExploração de Aplicações WebSegurança WebFuzzing
GitHubjvbotelho/cve-2026-69243-poc-aiohttp-smuggling

cve-2026-69243-poc-aiohttp-smuggling

Reproduz contrabando de requisições (CWE-444) em aiohttp por meio de upgrades WebSocket rejeitados, com payloads em Python/Rust e laboratório Docker demonstrando bypass do controle de acesso de proxy.

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Ver RepositórioSite
16há 1 mêsAinda não revisado
Compartilhar

CVE-2026-69243 — Smuggling de requisições em aiohttp (CWE-444)

Smuggling de requisições por meio de upgrade WebSocket rejeitado no aiohttp < 3.14.2. Quando um proxy reverso encaminha os cabeçalhos Connection: Upgrade + Upgrade: websocket, o parser vulnerável do aiohttp ignora o corpo da requisição e interpreta os bytes restantes como uma requisição em pipeline — contornando os controles de acesso de borda.

Corrigido no aiohttp 3.14.2 (commit 6ae358f).

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

Execução em 60 segundos

Copie e cole a versão Python (zero dependências, apenas 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>

Ou compile a versão Rust (zero dependências, apenas 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>

Binários pré-compilados serão publicados em Releases por meio do fluxo de trabalho de release.

Ambos produzem payloads byte-idênticos (garantido por um teste de paridade na CI). Se o laboratório estiver em execução:

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

Saída esperada: 1 resposta HTTP (WebSocket upgrade rejected). Em seguida, verifique:

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'

A contagem no backend > 0 e a contagem no Nginx = 0 significa que a divisão CWE-444 foi confirmada. Este PoC envia um único segmento TCP — o corpo é a requisição contrabandeada; o Nginx o trata como corpo, o aiohttp o trata como uma segunda requisição.

O que isto prova

  • Confusão no parser: o aiohttp 3.14.1 _http_parser.pyx retorna 2 (pular corpo) na detecção de upgrade antes que o corpo seja consumido (linha ~863). Os bytes do corpo permanecem em _message_tail e são realimentados ao parser em web_protocol.py finish_response (linha ~771).
  • Divisão CWE-444: o frontend vê 1 requisição com corpo; o backend vê 2 requisições em pipeline. Divergência na contagem de requisições nos logs.
  • Bypass de controle de acesso: quando o Nginx tem location /admin { deny all; }, o /admin contrabandeado ainda chega ao backend porque as decisões de roteamento do Nginx são tomadas apenas com base na requisição externa.
  • Nenhuma correção no nível do handler: await request.read() retorna 0 bytes em requisições de upgrade no 3.14.1 — o corpo é retido abaixo da camada do handler. Aplique o patch ou remova os cabeçalhos de upgrade no proxy em rotas que não devem trocar de protocolo.

Laboratório 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

Serviços:

ContainerFinalidade
backend-vulnaiohttp 3.14.1 (vulnerável), READ_BODY=false
backend-vuln-readaiohttp 3.14.1, READ_BODY=true (prova que o handler não resolve)
backend-patchedaiohttp 3.14.2 (corrigido)
nginx-upgradeEncaminha cabeçalhos de upgrade (deny all em /admin)
nginx-defaultSem encaminhamento de upgrade (neutraliza o bug)
nginx-stripRemove Connection "" (neutraliza o bug)
attackerBinários Rust: reproduce, fase2, poc

Resultados completos das Fases 1 a 3 em findings/.

Limitações (honestas)

  • O proxy precisa encaminhar os cabeçalhos de upgrade. Configurações do Nginx que enviam Connection: close ao backend ou removem os cabeçalhos Connection/Upgrade não são vulneráveis. A configuração que expõe a divisão é o trecho canônico map para WebSocket da própria documentação de proxy do Nginx.
  • A resposta contrabandeada é absorvida pelo proxy. Na cadeia Nginx demonstrada, o atacante recebe apenas a resposta externa /ws. A evidência do contrabando está nos logs do backend, não na resposta ao atacante — uma primitiva cega e unidirecional nessa topologia. Outras topologias de proxy não foram testadas.
  • O enquadramento chunked é, na prática, específico de CL. Diretamente contra o aiohttp, Transfer-Encoding: chunked NÃO contrabandeia — a linha bruta de tamanho de chunk (ex.: 3e) chega ao parser como um método inválido e a conexão morre. Através do Nginx funciona, mas por normalização: o Nginx remove o chunking do corpo e encaminha um Content-Length sintetizado, então o backend é explorado pelo mesmo caminho CL. A flag --chunked demonstra o caminho via proxy.
  • Requer um endpoint que rejeite upgrades WebSocket. O handler deve retornar uma resposta que não seja WebSocketResponse. A maioria dos apps que não usa WebSockets em uma rota rejeita por padrão (o framework retorna 404 ou passa para o próximo handler).

Arquivos

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

Detecção

Consulte findings/fase3-deteccao.md para a análise completa. Resumo, com as ressalvas que importam em produção:

  1. Divergência na contagem de requisições (backend > frontend) na mesma conexão. Observe que o backend registra o IP do proxy, não o do cliente — a correlação exige normalizar X-Forwarded-For/Proxy Protocol, ou registrar um ID de conexão upstream + a sequência de requisições por conexão. Apenas IP do cliente + janela de tempo é fraco (NAT, keep-alive, concorrência).
  2. Requisição no backend sem correspondência no frontend: um caminho restrito no log do backend sem entrada correspondente no log de acesso do frontend. Filtre sub-redes internas (health checks que contornam o proxy geram falso-positivo).
  3. Rejeição de upgrade + caminho diferente do mesmo cliente em ~100 ms. Confiança média — pipelining legítimo produz intervalos semelhantes; o logger do laboratório não registra porta remota/ID de conexão, então "sem novo handshake TCP" NÃO é algo que esses dados demonstram.
  4. Middleware de corpo retido (content_length vs bytes de request.read()): dispara no 3.14.1, fica silencioso no 3.14.2. Detecta, não mitiga. Defina o escopo (rotas candidatas a upgrade, corpos pequenos, status não-101) — a versão ingênua armazena todo corpo em memória.
  5. reqlen de borda acima da linha de base de cabeçalhos em endpoints WebSocket — baixa confiança isoladamente (cookies/JWT/cabeçalhos de rastreamento geram ruído), mas o único sinal de borda quando o cliente não envia Content-Length (entrada chunked).
Baixar ferramenta