
Proof-of-concept che riproduce CVE-2026-8932, una falla di corrispondenza incompleta nella configurazione mTLS nel riutilizzo delle connessioni di libcurl, con un server lab locale e un PoC in C.
Riproduzione proof-of-concept di CVE-2026-8932, un problema di corrispondenza incompleta della configurazione mTLS nel riutilizzo delle connessioni di libcurl.
CVE-2026-8932 riguarda la logica di riutilizzo delle connessioni di libcurl quando la configurazione mutual TLS (mTLS) cambia tra una richiesta e l'altra.
In condizioni vulnerabili, libcurl può riutilizzare una connessione TLS esistente anche se un'opzione di configurazione relativa a mTLS è cambiata e avrebbe dovuto impedire il riutilizzo di quella connessione.
Questo PoC dimostra il problema modificando la password della chiave privata tra due richieste, utilizzando lo stesso certificato client e la stessa chiave privata.
La prima richiesta utilizza la password corretta:
correct-password
La seconda richiesta utilizza intenzionalmente una password non valida:
WRONG-PASSWORD
Se libcurl riutilizza in modo errato la connessione TLS esistente, la seconda richiesta non richiede un altro handshake TLS. Di conseguenza, la password non valida della chiave privata non è mai necessaria per stabilire una nuova connessione TLS e la richiesta ha successo.
Il PoC dimostra quindi la condizione di riutilizzo della connessione associata a CVE-2026-8932.
| Proprietà | Valore |
|---|---|
| CVE | CVE-2026-8932 |
| Componente | libcurl |
| Tipo di vulnerabilità | Corrispondenza incompleta della configurazione mTLS |
| CWE | CWE-305 — Authentication Bypass by Primary Weakness |
| Area interessata | Riutilizzo della connessione TLS |
| Protocollo | HTTPS / mTLS |
| Client | API libcurl |
| curl CLI | Non interessato |
| Ambito del PoC | Ambiente di laboratorio locale |
La vulnerabilità è un problema di corrispondenza logica/di configurazione, non una tradizionale vulnerabilità di sicurezza della memoria come un buffer overflow o un use-after-free.
libcurl mantiene le informazioni sulla connessione e può riutilizzare una connessione esistente quando una richiesta successiva è considerata compatibile con la configurazione della connessione.
Per le connessioni TLS, la configurazione della connessione deve quindi essere confrontata con attenzione prima del riutilizzo.
L'implementazione vulnerabile non includeva tutti i campi rilevanti della configurazione mTLS nella logica di corrispondenza della connessione.
Tra le impostazioni interessate figurano:
cert_type
key
key_type
key_passwd
key_blob
L'impostazione importante utilizzata da questo PoC è:
key_passwd
Il PoC stabilisce una connessione TLS utilizzando una chiave privata client cifrata e la password corretta:
correct-password
Esegue poi un'altra richiesta utilizzando lo stesso certificato e la stessa chiave, ma cambia la password in:
WRONG-PASSWORD
Un'implementazione vulnerabile della corrispondenza delle connessioni può comunque considerare riutilizzabile la connessione esistente.
Il test consiste in due richieste HTTP.
La prima richiesta stabilisce la connessione TLS:
URL:
https://server.test:8443/A
Certificato client:
client.crt
Chiave privata:
client.key
Password della chiave:
correct-password
La connessione TLS rimane persistente perché il server supporta HTTP keep-alive.
La seconda richiesta utilizza lo stesso certificato e la stessa chiave privata, ma cambia la password della chiave:
URL:
https://server.test:8443/B
Certificato client:
client.crt
Chiave privata:
client.key
Password della chiave:
WRONG-PASSWORD
Se libcurl crea una nuova connessione TLS, il caricamento della chiave privata cifrata con la password errata dovrebbe fallire.
Tuttavia, se libcurl riutilizza in modo errato la connessione esistente, non è richiesto un nuovo handshake TLS.
La password non valida quindi non impedisce il successo della richiesta HTTP.
L'ambiente di laboratorio è composto da:
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'osservazione importante è che /A e /B devono arrivare sulla stessa connessione 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
La directory certs/ è intenzionalmente mantenuta vuota nel repository. I certificati e le chiavi private devono essere generati localmente.
Il PoC è stato testato nel seguente ambiente:
OS:
Debian GNU/Linux 13 (trixie)
Architecture:
x86_64
libcurl:
8.14.1
OpenSSL:
3.5.7
GCC:
14.2.0
L'installazione vulnerabile di libcurl utilizzata per la riproduzione era:
libcurl/8.14.1
Verificare la versione installata:
curl --version
e:
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
Generare la chiave privata del server:
openssl genrsa -out server.key 2048
Generare la CSR:
openssl req -new \
-key server.key \
-subj "/CN=server.test" \
-out server.csr
Creare le estensioni del certificato:
printf "subjectAltName=DNS:server.test\nextendedKeyUsage=serverAuth\n" \
> server.ext
Firmare il certificato utilizzando la CA di test:
openssl x509 -req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out server.crt \
-days 3650 \
-sha256 \
-extfile server.ext
Generare la chiave privata del client:
openssl genrsa -out client.key.tmp 2048
Generare la CSR:
openssl req -new \
-key client.key.tmp \
-subj "/CN=Client-A" \
-out client.csr
Convertire la chiave privata nel formato PKCS#8 cifrato:
openssl pkcs8 \
-topk8 \
-in client.key.tmp \
-out client.key \
-v2 aes-256-cbc \
-passout pass:correct-password
La chiave privata risultante è cifrata con:
correct-password
Creare le estensioni del certificato client:
printf "extendedKeyUsage=clientAuth\n" \
> client.ext
Firmare il certificato client:
openssl x509 -req \
-in client.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out client.crt \
-days 3650 \
-sha256 \
-extfile client.ext
A questo punto i file necessari dovrebbero esistere:
certs/
├── ca.crt
├── ca.key
├── client.crt
├── client.key
├── server.crt
└── server.key
Spostarsi nella directory del server:
cd ~/cve-2026-8932-lab/server
Avviare il server:
python3 server.py
Il server è in ascolto su:
0.0.0.0:8443
È richiesta l'autenticazione tramite certificato client.
Il server mantiene inoltre attive le connessioni HTTP/1.1 in modo che libcurl possa riutilizzare la connessione TLS.
Aprire un altro terminale:
cd ~/cve-2026-8932-lab/poc
Compilare:
gcc -Wall -Wextra -O0 -g \
poc.c \
$(pkg-config --cflags --libs libcurl) \
-o poc
Eseguire:
./poc
Il PoC utilizza:
Client certificate : client.crt
Private key : client.key
Request A password : correct-password
Request B password : WRONG-PASSWORD
La prova più importante lato client è:
[+] 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 richiesta riceve quindi:
< HTTP/1.1 200 OK
e il PoC segnala:
[!!!] B REQUEST SUCCEEDED
Questo è significativo perché /B utilizza una password della chiave privata intenzionalmente non valida.
L'osservazione chiave non è semplicemente che /B ha successo.
La prova critica è che libcurl segnala esplicitamente:
Re-using existing https: connection
Pertanto la seconda richiesta non deve eseguire un altro handshake TLS utilizzando la configurazione della chiave modificata.
L'output del server fornisce una conferma indipendente del riutilizzo della connessione.
L'output rilevante è:
[+] 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'osservazione importante è:
/A -> Connection #2
/B -> Connection #2
Entrambe le richieste HTTP sono quindi state ricevute attraverso la stessa connessione TLS.
Il server segnala inoltre la stessa identità client:
Client-A
per entrambe le richieste.
La chiave privata utilizzata dal PoC è cifrata.
La prima richiesta utilizza:
correct-password
che consente a libcurl/OpenSSL di accedere alla chiave privata e stabilire la connessione TLS.
La seconda richiesta modifica la configurazione in:
WRONG-PASSWORD
Se dovesse essere stabilita una nuova connessione TLS, libcurl dovrebbe elaborare nuovamente la chiave privata cifrata e la password errata dovrebbe causare il fallimento dell'operazione.
Tuttavia, quando la connessione TLS esistente viene riutilizzata, la sessione TLS è già stabilita.
Nessuna nuova operazione di autenticazione client è necessaria per /B.
Concettualmente:
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
Questo PoC dimostra il riutilizzo della connessione, non la ripresa della sessione TLS.
I due meccanismi sono diversi.
Una connessione TLS già stabilita rimane aperta e viene utilizzata per un'altra richiesta HTTP:
TLS connection #2
|
+-- GET /A
|
+-- GET /B
Non è richiesto un secondo handshake TLS.
Viene creata una nuova connessione TCP/TLS, ma le informazioni crittografiche di sessione di una connessione precedente vengono utilizzate per abbreviare il nuovo handshake TLS.
Non è ciò su cui si basa questo PoC.
Il PoC condivide intenzionalmente:
CURL_LOCK_DATA_CONNECT
tra i due easy handle senza condividere:
CURL_LOCK_DATA_SSL_SESSION
Questo mantiene la dimostrazione focalizzata sul riutilizzo della connessione.
Il repository contiene l'output catturato dalla riproduzione riuscita.

L'output catturato dimostra:
/A riuscitaRe-using existing https: connection/B riuscitaL'output completo del terminale è disponibile anche in:
docs/poc-output.txt

Le prove lato server dimostrano che:
/A -> TLS connection #2
/B -> TLS connection #2
L'output completo del server è disponibile in:
docs/server-output.txt
Se viene eseguito un test manuale con curl prima di eseguire il PoC, il server potrebbe mostrare una connessione precedente:
TLS connection #1
Questa connessione non è correlata all'esecuzione effettiva del PoC.
Ad esempio, la richiesta di validazione manuale:
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
può creare la connessione #1.
Il PoC effettivo può quindi creare la connessione #2.
La condizione rilevante non è quindi il numero assoluto della connessione, ma che:
/A and /B
compaiano sulla stessa connessione TLS durante l'esecuzione del PoC.
Il fix upstream è:
7541ae569d82fb308a5e2d94916027da4fa3ba3e
Commit:
tls: fix incomplete mTLS config in conn reuse and session cache
Il fix aggiunge i confronti mancanti della configurazione mTLS alla logica di corrispondenza delle connessioni.
Concettualmente, la logica di corrispondenza ora considera valori tra cui:
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)
La modifica importante per questo PoC è il confronto di:
key_passwd
Una modifica alla password della chiave privata dovrebbe quindi impedire che la connessione esistente venga considerata compatibile per il riutilizzo.
Il fix upstream ha inoltre introdotto una copertura di regressione per il comportamento di corrispondenza della configurazione TLS.
Il test rilevante è associato a:
test 3303
Il test di regressione copre le differenze nella configurazione mTLS, tra cui:
key_passwd
key
key_type
cert_type
Ad esempio, configurazioni identiche dovrebbero corrispondere:
config A == config B
mentre la modifica della password della chiave non dovrebbe:
config A key_passwd = password-A
config B key_passwd = password-B
↓
connection match = false
Questa è la stessa dimensione di configurazione esercitata da questo PoC.
/B fallisceSe /B fallisce, verificare innanzitutto se libcurl ha effettivamente riutilizzato la connessione.
L'output verboso dovrebbe contenere:
Re-using existing https: connection
Se questa riga non compare, la condizione di vulnerabilità non è stata dimostrata.
/A e /B compaiono su connessioni TLS diverseIl server dovrebbe mostrare:
Connection #X -> /A
Connection #X -> /B
Se invece si vede:
Connection #X -> /A
Connection #Y -> /B
allora la seconda richiesta ha creato una nuova connessione TLS e la condizione prevista non è stata riprodotta.
Verificare che:
CURL_LOCK_DATA_CONNECT sia condiviso.CURLOPT_FORBID_REUSE non sia abilitato.CURLOPT_FRESH_CONNECT non sia abilitato.Connection: keep-alive.La chiave privata del client deve essere generata utilizzando:
correct-password
Il PoC utilizza poi intenzionalmente:
WRONG-PASSWORD
Non modificare la password incorporata nel comando di generazione della chiave privata a meno che non venga modificato anche il valore corrispondente nel PoC.
Questo repository è destinato a ricerche di sicurezza controllate e alla riproduzione di vulnerabilità.
Il PoC è progettato per operare contro un server di test ospitato localmente:
127.0.0.1:8443
Non utilizzare il PoC contro sistemi senza autorizzazione.
Le chiavi private e i certificati generati dovrebbero rimanere al di fuori del controllo di versione.
Il repository dovrebbe contenere solo:
certs/.gitkeep
anziché chiavi private generate.
La progettazione del PoC, la metodologia di laboratorio, l'implementazione e l'analisi tecnica sono state sviluppate da AliReza con l'assistenza di OpenAI ChatGPT (GPT-5.6 Luna).
La verifica umana, l'esecuzione, il testing e la riproduzione sono stati eseguiti dall'autore del repository.
Questo repository è fornito per scopi di ricerca sulla sicurezza, analisi di vulnerabilità e finalità educative.
Utilizzarlo solo in sistemi e ambienti per i quali si dispone di autorizzazione esplicita.