Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
cve-2026-69243-poc-aiohttp-smuggling — Reproduit le smuggling de requêtes CWE-444 dans aiohttp via des mises à niveau WebSocket rejetées, avec des payloads Python/Rust et un laboratoire Docker démontrant le contournement du contrôle d'accès du proxy. | Kitploit
Outils/GitHubGitHub/jvbotelho/cve-2026-69243-poc-aiohttp-smuggling
Génération de PayloadsAnalyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebFuzzing
GitHubjvbotelho/cve-2026-69243-poc-aiohttp-smuggling

cve-2026-69243-poc-aiohttp-smuggling

Reproduit le smuggling de requêtes CWE-444 dans aiohttp via des mises à niveau WebSocket rejetées, avec des payloads Python/Rust et un laboratoire Docker démontrant le contournement du contrôle d'accès du proxy.

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôtSite web
1il y a 16 joursPas encore vérifié

CVE-2026-69243 — Contrebande de requêtes dans aiohttp (CWE-444)

Contrebande de requêtes via une mise à niveau WebSocket rejetée dans aiohttp < 3.14.2. Lorsqu'un proxy inverse transmet les en-têtes Connection: Upgrade + Upgrade: websocket, l'analyseur aiohttp vulnérable ignore le corps de la requête et interprète les octets suivants comme une requête pipelinée — contournant les contrôles d'accès en périphérie.

Corrigé dans aiohttp 3.14.2 (commit 6ae358f).

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

Exécution en 60 secondes

Copiez-collez la version Python (zéro dépendance, uniquement la bibliothèque standard) :

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 compilez la version Rust (zéro dépendance, uniquement la bibliothèque standard) :

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>

Des binaires précompilés seront publiés dans Releases via le workflow de publication.

Les deux produisent des payloads octet pour octet identiques (garanti par un test de parité CI). Si le lab tourne :

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

Sortie attendue : 1 réponse HTTP (WebSocket upgrade rejected). Ensuite, vérifiez :

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 compteur backend > 0 et un compteur Nginx = 0 signifient que la scission CWE-444 est confirmée. Ce PoC envoie un seul segment TCP — le corps est la requête introduite en contrebande ; Nginx le traite comme un corps, aiohttp le traite comme une seconde requête.

Ce que cela prouve

  • Confusion de l'analyseur : aiohttp 3.14.1 _http_parser.pyx retourne 2 (ignorer le corps) lors de la détection de la mise à niveau avant que le corps ne soit consommé (ligne ~863). Les octets du corps restent dans _message_tail et sont renvoyés à l'analyseur dans web_protocol.py finish_response (ligne ~771).
  • Scission CWE-444 : le frontal voit 1 requête avec corps ; le backend voit 2 requêtes pipelinées. Écart du nombre de requêtes dans les journaux.
  • Contournement des contrôles d'accès : lorsque Nginx a location /admin { deny all; }, le /admin introduit en contrebande atteint toujours le backend car les décisions de routage de Nginx sont prises uniquement sur la requête externe.
  • Aucun correctif au niveau du gestionnaire : await request.read() retourne 0 octet sur les requêtes de mise à niveau dans 3.14.1 — le corps est retenu sous la couche du gestionnaire. Corrigez, ou supprimez les en-têtes de mise à niveau au niveau du proxy sur les routes qui ne doivent pas changer de protocole.

Laboratoire complet

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

Services :

Résultats complets des phases 1 à 3 dans findings/.

Limites (en toute honnêteté)

  • Le proxy doit transmettre les en-têtes de mise à niveau. Les configurations Nginx qui envoient Connection: close au backend ou suppriment les en-têtes Connection/Upgrade ne sont pas vulnérables. La configuration qui expose la scission est l'extrait map WebSocket canonique de la documentation de proxy de Nginx.
  • La réponse introduite en contrebande est absorbée par le proxy. Dans la chaîne Nginx démontrée, l'attaquant ne reçoit que la réponse externe /ws. La preuve de la contrebande se trouve dans les journaux du backend, pas dans la réponse à l'attaquant — une primitive aveugle à sens unique dans cette topologie. D'autres topologies de proxy n'ont pas été testées.
  • Le cadrage en morceaux (chunked) est spécifique à CL dans son effet. Directement contre aiohttp, Transfer-Encoding: chunked ne fait PAS de contrebande — la ligne brute de taille de morceau (par ex. 3e) atteint l'analyseur comme une méthode invalide et la connexion meurt. Via Nginx, cela fonctionne, mais par normalisation : Nginx dé-chunke le corps et transmet un Content-Length synthétisé, donc le backend est exploité par le même chemin CL. L'option --chunked démontre le chemin via le proxy.
  • Nécessite un point de terminaison qui rejette les mises à niveau WebSocket. Le gestionnaire doit retourner autre chose qu'une . La plupart des applications qui n'utilisent pas les WebSockets sur une route rejettent par défaut (le framework retourne 404 ou passe au gestionnaire suivant).

Fichiers

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

Détection

Voir findings/fase3-deteccao.md pour l'analyse complète. Résumé, avec les mises en garde qui comptent en production :

  1. Écart du nombre de requêtes (backend > frontend) sur la même connexion. Notez que le backend journalise l'IP du proxy, pas celle du client — la corrélation nécessite de normaliser X-Forwarded-For/Proxy Protocol, ou de journaliser un ID de connexion en amont + la séquence de requêtes par connexion. L'IP du client + la fenêtre temporelle, à elles seules, constituent une preuve faible (NAT, keep-alive, concurrence).
  2. Requête backend sans correspondance frontend : un chemin restreint dans le journal backend sans entrée correspondante dans le journal d'accès du frontend. Filtrez les sous-réseaux internes (les contrôles de santé contournant le proxy créent des faux positifs).
  3. Rejet de mise à niveau + chemin différent depuis le même client en ~100 ms. Confiance moyenne — le pipelining légitime produit des intervalles similaires ; le journaliseur du lab ne consigne pas le port distant/ID de connexion, donc « aucune nouvelle poignée de main TCP » n'est PAS démontré par ces données.
  4. Middleware de corps retenu (content_length vs octets de request.read()) : se déclenche sur 3.14.1, silencieux sur 3.14.2. Détecte, ne mitige pas. Limitez son périmètre (routes candidates à la mise à niveau, petits corps, statuts non-101) — la version naïve met en mémoire tampon chaque corps.
  5. reqlen de périphérie au-dessus de la ligne de base des en-têtes sur les points de terminaison WebSocket — faible confiance à elle seule (cookies/JWT/en-têtes de traçage sont bruyants), mais le seul signal périphérique lorsque le client n'envoie pas de Content-Length (entrée chunked).
Télécharger l’outil
ContainerRôle
backend-vulnaiohttp 3.14.1 (vulnérable), READ_BODY=false
backend-vuln-readaiohttp 3.14.1, READ_BODY=true (prouve que le gestionnaire ne peut pas aider)
backend-patchedaiohttp 3.14.2 (corrigé)
nginx-upgradeTransmet les en-têtes de mise à niveau (deny all sur /admin)
nginx-defaultAucune transmission de mise à niveau (neutralise le bug)
nginx-stripSuppression de Connection "" (neutralise le bug)
attackerBinaires Rust : reproduce, fase2, poc
WebSocketResponse