
Preuve de concept reproduisant CVE-2026-8932, une faille de correspondance de configuration mTLS incomplète dans la réutilisation de connexion de libcurl, avec un serveur de laboratoire local et un PoC en C.
Reproduction de preuve de concept de CVE-2026-8932, un problème de correspondance de configuration mTLS incomplète dans la réutilisation de connexion de libcurl.
CVE-2026-8932 affecte la logique de réutilisation de connexion de libcurl lorsque la configuration mTLS (TLS mutuel) change entre les requêtes.
Dans des conditions vulnérables, libcurl peut réutiliser une connexion TLS existante même si une option de configuration liée à mTLS a changé et aurait dû empêcher la réutilisation de cette connexion.
Ce PoC démontre le problème en changeant le mot de passe de la clé privée entre deux requêtes tout en utilisant le même certificat client et la même clé privée.
La première requête utilise le mot de passe correct :
correct-password
La seconde requête utilise intentionnellement un mot de passe invalide :
WRONG-PASSWORD
Si libcurl réutilise incorrectement la connexion TLS existante, la seconde requête ne nécessite pas un autre handshake TLS. Par conséquent, le mot de passe invalide de la clé privée n'est jamais nécessaire pour établir une nouvelle connexion TLS et la requête réussit.
Le PoC démontre donc la condition de réutilisation de connexion associée à CVE-2026-8932.
| Propriété | Valeur |
|---|
| CVE | CVE-2026-8932 |
| Composant | libcurl |
| Type de vulnérabilité | Correspondance de configuration mTLS incomplète |
| CWE | CWE-305 — Contournement d'authentification par faiblesse primaire |
| Zone affectée | Réutilisation de connexion TLS |
| Protocole | HTTPS / mTLS |
| Client | API libcurl |
| CLI curl | Non affecté |
| Portée du PoC | Environnement de laboratoire local |
La vulnérabilité est un problème de correspondance logique/configuration, plutôt qu'une vulnérabilité traditionnelle de sécurité mémoire telle qu'un débordement de tampon ou un use-after-free.
libcurl maintient les informations de connexion et peut réutiliser une connexion existante lorsqu'une requête ultérieure est considérée comme compatible avec la configuration de la connexion.
Pour les connexions TLS, la configuration de la connexion doit donc être comparée avec soin avant réutilisation.
L'implémentation vulnérable n'incluait pas tous les champs de configuration mTLS pertinents dans la logique de correspondance de connexion.
Parmi les paramètres affectés figurent :
cert_type
key
key_type
key_passwd
key_blob
Le paramètre important utilisé par ce PoC est :
key_passwd
Le PoC établit une connexion TLS en utilisant une clé privée client chiffrée et le mot de passe correct :
correct-password
Il effectue ensuite une autre requête en utilisant le même certificat et la même clé mais change le mot de passe pour :
WRONG-PASSWORD
Une implémentation vulnérable de correspondance de connexion peut toujours considérer la connexion existante comme réutilisable.
Le test consiste en deux requêtes HTTP.
La première requête établit la connexion TLS :
URL:
https://server.test:8443/A
Certificat client :
client.crt
Clé privée :
client.key
Mot de passe de la clé :
correct-password
La connexion TLS reste persistante car le serveur prend en charge HTTP keep-alive.
La seconde requête utilise le même certificat et la même clé privée mais change le mot de passe de la clé :
URL:
https://server.test:8443/B
Certificat client :
client.crt
Clé privée :
client.key
Mot de passe de la clé :
WRONG-PASSWORD
Si libcurl crée une nouvelle connexion TLS, le chargement de la clé privée chiffrée avec le mauvais mot de passe devrait échouer.
Cependant, si libcurl réutilise incorrectement la connexion existante, aucun nouveau handshake TLS n'est nécessaire.
Le mot de passe invalide n'empêche donc pas la requête HTTP de réussir.
La configuration du laboratoire consiste en :
localhost
|
v
+---------------------+
| Python mTLS Server |
| 127.0.0.1:8443 |
+----------+----------+
|
| HTTPS / TLS 1.3
|
+--------+--------+
| |
/A /B
\ /
\ /
v v
Same TLS Connection
|
v
libcurl
PoC (C)
L'observation importante est que /A et /B doivent arriver sur la même connexion TLS.
CVE-2026-8932-PoC/
├── README.md
├── LICENSE
├── .gitignore
│
├── certs/
│ └── .gitkeep
│
├── poc/
│ └── poc.c
│
├── server/
│ └── server.py
│
└── docs/
├── CVE-2026-8932-POC.jpg
├── CVE-2026-8932-server-POC.jpg
├── poc-output.txt
└── server-output.txt
Le répertoire certs/ est intentionnellement laissé vide dans le dépôt. Les certificats et les clés privées doivent être générés localement.
Le PoC a été testé dans l'environnement suivant :
OS:
Debian GNU/Linux 13 (trixie)
Architecture:
x86_64
libcurl:
8.14.1
OpenSSL:
3.5.7
GCC:
14.2.0
L'installation vulnérable de libcurl utilisée pour la reproduction était :
libcurl/8.14.1
Vérifiez la version installée :
curl --version
et :
pkg-config --modversion libcurl
mkdir -p ~/cve-2026-8932-lab/{certs,poc,server,docs}
cd ~/cve-2026-8932-lab
cd ~/cve-2026-8932-lab/certs
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes \
-key ca.key \
-sha256 \
-days 3650 \
-subj "/CN=CVE-2026-8932-CA" \
-out ca.crt
Générez la clé privée du serveur :
openssl genrsa -out server.key 2048
Générez la CSR :
openssl req -new \
-key server.key \
-subj "/CN=server.test" \
-out server.csr
Créez les extensions du certificat :
printf "subjectAltName=DNS:server.test\nextendedKeyUsage=serverAuth\n" \
> server.ext
Signez le certificat à l'aide de la CA de test :
openssl x509 -req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out server.crt \
-days 3650 \
-sha256 \
-extfile server.ext
Générez la clé privée du client :
openssl genrsa -out client.key.tmp 2048
Générez la CSR :
openssl req -new \
-key client.key.tmp \
-subj "/CN=Client-A" \
-out client.csr
Convertissez la clé privée au format PKCS#8 chiffré :
openssl pkcs8 \
-topk8 \
-in client.key.tmp \
-out client.key \
-v2 aes-256-cbc \
-passout pass:correct-password
La clé privée résultante est chiffrée avec :
correct-password
Créez les extensions du certificat client :
printf "extendedKeyUsage=clientAuth\n" \
> client.ext
Signez le certificat client :
openssl x509 -req \
-in client.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out client.crt \
-days 3650 \
-sha256 \
-extfile client.ext
À ce stade, les fichiers requis devraient exister :
certs/
├── ca.crt
├── ca.key
├── client.crt
├── client.key
├── server.crt
└── server.key
Déplacez-vous dans le répertoire du serveur :
cd ~/cve-2026-8932-lab/server
Démarrez le serveur :
python3 server.py
Le serveur écoute sur :
0.0.0.0:8443
L'authentification par certificat client est requise.
Le serveur maintient également les connexions HTTP/1.1 en vie afin que libcurl puisse réutiliser la connexion TLS.
Ouvrez un autre terminal :
cd ~/cve-2026-8932-lab/poc
Compilez :
gcc -Wall -Wextra -O0 -g \
poc.c \
$(pkg-config --cflags --libs libcurl) \
-o poc
Exécutez :
./poc
Le PoC utilise :
Certificat client : client.crt
Clé privée : client.key
Mot de passe requête A : correct-password
Mot de passe requête B : WRONG-PASSWORD
La preuve côté client la plus importante est :
[+] STEP 2: Request using modified mTLS configuration
[+] Key password = WRONG-PASSWORD
* Re-using existing https: connection with host server.test
> GET /B HTTP/1.1
La requête reçoit ensuite :
< HTTP/1.1 200 OK
et le PoC rapporte :
[!!!] B REQUEST SUCCEEDED
Ceci est significatif car /B utilise un mot de passe de clé privée intentionnellement invalide.
L'observation clé n'est pas simplement que /B réussit.
La preuve critique est que libcurl rapporte explicitement :
Re-using existing https: connection
Par conséquent, la seconde requête n'a pas besoin d'effectuer un autre handshake TLS en utilisant la configuration de clé modifiée.
La sortie du serveur fournit une confirmation indépendante de la réutilisation de connexion.
La sortie pertinente est :
[+] TLS connection #2
Client CN : Client-A
[+] Connection #2 Client=Client-A Request=GET /A HTTP/1.1
[+] Connection #2 Client=Client-A Request=GET /B HTTP/1.1
L'observation importante est :
/A -> Connection #2
/B -> Connection #2
Les deux requêtes HTTP ont donc été reçues via la même connexion TLS.
Le serveur rapporte également la même identité client :
Client-A
pour les deux requêtes.
La clé privée utilisée par le PoC est chiffrée.
La première requête utilise :
correct-password
ce qui permet à libcurl/OpenSSL d'accéder à la clé privée et d'établir la connexion TLS.
La seconde requête change la configuration pour :
WRONG-PASSWORD
Si une nouvelle connexion TLS devait être établie, libcurl devrait traiter à nouveau la clé privée chiffrée et le mot de passe incorrect devrait provoquer l'échec de l'opération.
Cependant, lorsque la connexion TLS existante est réutilisée, la session TLS est déjà établie.
Aucune nouvelle opération d'authentification client n'est nécessaire pour /B.
Conceptuellement :
Request A
|
| correct-password
v
TLS handshake
|
v
Established TLS connection
|
+----------------------+
| |
v v
/A /B
|
WRONG-PASSWORD
|
X
No new TLS handshake
|
v
HTTP succeeds
Ce PoC démontre la réutilisation de connexion, et non la reprise de session TLS.
Les deux mécanismes sont différents.
Une connexion TLS déjà établie reste ouverte et est utilisée pour une autre requête HTTP :
TLS connection #2
|
+-- GET /A
|
+-- GET /B
Aucun second handshake TLS n'est nécessaire.
Une nouvelle connexion TCP/TLS est créée, mais les informations de session cryptographique d'une connexion antérieure sont utilisées pour raccourcir le nouveau handshake TLS.
Ce n'est pas ce sur quoi ce PoC repose.
Le PoC partage intentionnellement :
CURL_LOCK_DATA_CONNECT
entre les deux easy handles sans partager :
CURL_LOCK_DATA_SSL_SESSION
Cela maintient la démonstration centrée sur la réutilisation de connexion.
Le dépôt contient la sortie capturée de la reproduction réussie.

La sortie capturée démontre :
/A réussieRe-using existing https: connection/B réussieLa sortie complète du terminal est également disponible dans :
docs/poc-output.txt

La preuve côté serveur démontre que :
/A -> TLS connection #2
/B -> TLS connection #2
La sortie complète du serveur est disponible dans :
docs/server-output.txt
Si un test curl manuel est effectué avant d'exécuter le PoC, le serveur peut afficher une connexion antérieure :
TLS connection #1
Cette connexion n'est pas liée à l'exécution réelle du PoC.
Par exemple, la requête de validation manuelle :
curl \
--resolve server.test:8443:127.0.0.1 \
--cacert ~/cve-2026-8932-lab/certs/ca.crt \
--cert ~/cve-2026-8932-lab/certs/client.crt \
--key ~/cve-2026-8932-lab/certs/client.key \
--pass correct-password \
https://server.test:8443/A
peut créer la connexion #1.
Le PoC réel peut ensuite créer la connexion #2.
La condition pertinente n'est donc pas le numéro absolu de connexion, mais que :
/A and /B
apparaissent sur la même connexion TLS pendant l'exécution du PoC.
Le correctif amont est :
7541ae569d82fb308a5e2d94916027da4fa3ba3e
Commit :
tls: fix incomplete mTLS config in conn reuse and session cache
Le correctif ajoute les comparaisons de configuration mTLS manquantes à la logique de correspondance de connexion.
Conceptuellement, la logique de correspondance considère désormais des valeurs incluant :
blobcmp(c1->key_blob, c2->key_blob) &&
curl_strequal(c1->cert_type, c2->cert_type) &&
Curl_safecmp(c1->key, c2->key) &&
curl_strequal(c1->key_type, c2->key_type) &&
!Curl_timestrcmp(c1->key_passwd, c2->key_passwd)
Le changement important pour ce PoC est la comparaison de :
key_passwd
Une modification du mot de passe de la clé privée devrait donc empêcher la connexion existante d'être considérée comme compatible pour réutilisation.
Le correctif amont a également introduit une couverture de régression pour le comportement de correspondance de configuration TLS.
Le test pertinent est associé à :
test 3303
Les tests de régression couvrent les différences de configuration mTLS incluant :
key_passwd
key
key_type
cert_type
Par exemple, des configurations identiques devraient correspondre :
config A == config B
tandis que changer le mot de passe de la clé ne devrait pas :
config A key_passwd = password-A
config B key_passwd = password-B
↓
connection match = false
C'est la même dimension de configuration exercée par ce PoC.
/B échoueSi /B échoue, vérifiez d'abord si libcurl a réellement réutilisé la connexion.
La sortie verbeuse devrait contenir :
Re-using existing https: connection
Si cette ligne n'apparaît pas, la condition de vulnérabilité n'a pas été démontrée.
/A et /B apparaissent sur différentes connexions TLSLe serveur devrait afficher :
Connection #X -> /A
Connection #X -> /B
Si au lieu de cela vous voyez :
Connection #X -> /A
Connection #Y -> /B
alors la seconde requête a créé une nouvelle connexion TLS et la condition souhaitée n'a pas été reproduite.
Vérifiez que :
CURL_LOCK_DATA_CONNECT est partagé.CURLOPT_FORBID_REUSE n'est pas activé.CURLOPT_FRESH_CONNECT n'est pas activé.Connection: keep-alive.La clé privée du client doit être générée en utilisant :
correct-password
Le PoC utilise ensuite intentionnellement :
WRONG-PASSWORD
Ne modifiez pas le mot de passe intégré dans la commande de génération de clé privée à moins que la valeur correspondante du PoC ne soit également modifiée.
Ce dépôt est destiné à la recherche en sécurité contrôlée et à la reproduction de vulnérabilités.
Le PoC est conçu pour fonctionner contre un serveur de test hébergé localement :
127.0.0.1:8443
N'utilisez pas le PoC contre des systèmes sans autorisation.
Les clés privées et les certificats générés doivent rester en dehors du contrôle de version.
Le dépôt ne doit contenir que :
certs/.gitkeep
plutôt que des clés privées générées.
La conception du PoC, la méthodologie de laboratoire, l'implémentation et l'analyse technique ont été développées par AliReza avec l'assistance d'OpenAI ChatGPT (GPT-5.6 Luna).
La vérification humaine, l'exécution, les tests et la reproduction ont été effectués par l'auteur du dépôt.
Ce dépôt est fourni à des fins de recherche en sécurité, d'analyse de vulnérabilités et d'éducation.
Utilisez-le uniquement dans des systèmes et environnements pour lesquels vous disposez d'une autorisation explicite.