
Proof-of-concept reproducing CVE-2026-8932, an incomplete mTLS configuration matching flaw in libcurl connection reuse, with a local lab server and C PoC.
Proof-of-concept reproduction of CVE-2026-8932, an incomplete mTLS configuration matching issue in libcurl connection reuse.
CVE-2026-8932 affects libcurl's connection reuse logic when mutual TLS (mTLS) configuration changes between requests.
Under vulnerable conditions, libcurl can reuse an existing TLS connection even though an mTLS-related configuration option has changed and should have prevented that connection from being reused.
This PoC demonstrates the issue by changing the private-key password between two requests while using the same client certificate and private key.
The first request uses the correct password:
correct-password
The second request intentionally uses an invalid password:
WRONG-PASSWORD
If libcurl incorrectly reuses the existing TLS connection, the second request does not require another TLS handshake. Consequently, the invalid private-key password is never needed to establish a new TLS connection and the request succeeds.
The PoC therefore demonstrates the connection-reuse condition associated with CVE-2026-8932.
| Property | Value |
|---|
| CVE | CVE-2026-8932 |
| Component | libcurl |
| Vulnerability Type | Incomplete mTLS configuration matching |
| CWE | CWE-305 — Authentication Bypass by Primary Weakness |
| Affected Area | TLS connection reuse |
| Protocol | HTTPS / mTLS |
| Client | libcurl API |
| curl CLI | Not affected |
| PoC Scope | Local laboratory environment |
The vulnerability is a logic/configuration matching issue, rather than a traditional memory-safety vulnerability such as a buffer overflow or use-after-free.
libcurl maintains connection information and can reuse an existing connection when a subsequent request is considered compatible with the connection's configuration.
For TLS connections, the connection configuration must therefore be compared carefully before reuse.
The vulnerable implementation did not include all relevant mTLS configuration fields in the connection matching logic.
Among the affected settings are:
cert_type
key
key_type
key_passwd
key_blob
The important setting used by this PoC is:
key_passwd
The PoC establishes a TLS connection using an encrypted client private key and the correct password:
correct-password
It then performs another request using the same certificate and key but changes the password to:
WRONG-PASSWORD
A vulnerable connection-matching implementation may still consider the existing connection reusable.
The test consists of two HTTP requests.
The first request establishes the TLS connection:
URL:
https://server.test:8443/A
Client certificate:
client.crt
Private key:
client.key
Key password:
correct-password
The TLS connection remains persistent because the server supports HTTP keep-alive.
The second request uses the same certificate and private key but changes the key password:
URL:
https://server.test:8443/B
Client certificate:
client.crt
Private key:
client.key
Key password:
WRONG-PASSWORD
If libcurl creates a new TLS connection, loading the encrypted private key with the wrong password should fail.
However, if libcurl incorrectly reuses the existing connection, no new TLS handshake is required.
The invalid password therefore does not prevent the HTTP request from succeeding.
The laboratory setup consists of:
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)
The important observation is that /A and /B must arrive on the same TLS connection.
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
The certs/ directory is intentionally kept empty in the repository. Certificates and private keys should be generated locally.
The PoC was tested in the following environment:
OS:
Debian GNU/Linux 13 (trixie)
Architecture:
x86_64
libcurl:
8.14.1
OpenSSL:
3.5.7
GCC:
14.2.0
The vulnerable libcurl installation used for the reproduction was:
libcurl/8.14.1
Verify the installed version:
curl --version
and:
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
Generate the server private key:
openssl genrsa -out server.key 2048
Generate the CSR:
openssl req -new \
-key server.key \
-subj "/CN=server.test" \
-out server.csr
Create the certificate extensions:
printf "subjectAltName=DNS:server.test\nextendedKeyUsage=serverAuth\n" \
> server.ext
Sign the certificate using the test CA:
openssl x509 -req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out server.crt \
-days 3650 \
-sha256 \
-extfile server.ext
Generate the client private key:
openssl genrsa -out client.key.tmp 2048
Generate the CSR:
openssl req -new \
-key client.key.tmp \
-subj "/CN=Client-A" \
-out client.csr
Convert the private key into encrypted PKCS#8 format:
openssl pkcs8 \
-topk8 \
-in client.key.tmp \
-out client.key \
-v2 aes-256-cbc \
-passout pass:correct-password
The resulting private key is encrypted with:
correct-password
Create the client certificate extensions:
printf "extendedKeyUsage=clientAuth\n" \
> client.ext
Sign the client certificate:
openssl x509 -req \
-in client.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out client.crt \
-days 3650 \
-sha256 \
-extfile client.ext
At this point the required files should exist:
certs/
├── ca.crt
├── ca.key
├── client.crt
├── client.key
├── server.crt
└── server.key
Move into the server directory:
cd ~/cve-2026-8932-lab/server
Start the server:
python3 server.py
The server listens on:
0.0.0.0:8443
Client certificate authentication is required.
The server also keeps HTTP/1.1 connections alive so that libcurl can reuse the TLS connection.
Open another terminal:
cd ~/cve-2026-8932-lab/poc
Compile:
gcc -Wall -Wextra -O0 -g \
poc.c \
$(pkg-config --cflags --libs libcurl) \
-o poc
Execute:
./poc
The PoC uses:
Client certificate : client.crt
Private key : client.key
Request A password : correct-password
Request B password : WRONG-PASSWORD
The most important client-side evidence is:
[+] 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
The request then receives:
< HTTP/1.1 200 OK
and the PoC reports:
[!!!] B REQUEST SUCCEEDED
This is significant because /B uses an intentionally invalid private-key password.
The key observation is not simply that /B succeeds.
The critical evidence is that libcurl explicitly reports:
Re-using existing https: connection
Therefore the second request does not need to perform another TLS handshake using the modified key configuration.
The server output provides independent confirmation of the connection reuse.
The relevant output is:
[+] 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
The important observation is:
/A -> Connection #2
/B -> Connection #2
Both HTTP requests were therefore received through the same TLS connection.
The server also reports the same client identity:
Client-A
for both requests.
The private key used by the PoC is encrypted.
The first request uses:
correct-password
which allows libcurl/OpenSSL to access the private key and establish the TLS connection.
The second request changes the configuration to:
WRONG-PASSWORD
If a new TLS connection had to be established, libcurl would need to process the encrypted private key again and the incorrect password should cause the operation to fail.
However, when the existing TLS connection is reused, the TLS session is already established.
No new client authentication operation is necessary for /B.
Conceptually:
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
This PoC demonstrates connection reuse, not TLS session resumption.
The two mechanisms are different.
An already-established TLS connection remains open and is used for another HTTP request:
TLS connection #2
|
+-- GET /A
|
+-- GET /B
No second TLS handshake is required.
A new TCP/TLS connection is created, but cryptographic session information from an earlier connection is used to shorten the new TLS handshake.
That is not what this PoC relies on.
The PoC intentionally shares:
CURL_LOCK_DATA_CONNECT
between the two easy handles while not sharing:
CURL_LOCK_DATA_SSL_SESSION
This keeps the demonstration focused on connection reuse.
The repository contains captured output from the successful reproduction.

The captured output demonstrates:
/A requestRe-using existing https: connection/B requestThe complete terminal output is also available in:
docs/poc-output.txt

The server-side evidence demonstrates that:
/A -> TLS connection #2
/B -> TLS connection #2
The complete server output is available in:
docs/server-output.txt
If a manual curl test is performed before running the PoC, the server may display an earlier connection:
TLS connection #1
This connection is unrelated to the actual PoC execution.
For example, the manual validation request:
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
may create connection #1.
The actual PoC may then create connection #2.
The relevant condition is therefore not the absolute connection number, but that:
/A and /B
appear on the same TLS connection during the PoC execution.
The upstream fix is:
7541ae569d82fb308a5e2d94916027da4fa3ba3e
Commit:
tls: fix incomplete mTLS config in conn reuse and session cache
The fix adds the missing mTLS configuration comparisons to the connection matching logic.
Conceptually, the matching logic now considers values including:
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)
The important change for this PoC is the comparison of:
key_passwd
A change to the private-key password should therefore prevent the existing connection from being considered compatible for reuse.
The upstream fix also introduced regression coverage for the TLS configuration matching behavior.
The relevant test is associated with:
test 3303
The regression testing covers differences in mTLS configuration including:
key_passwd
key
key_type
cert_type
For example, identical configurations should match:
config A == config B
while changing the key password should not:
config A key_passwd = password-A
config B key_passwd = password-B
↓
connection match = false
This is the same configuration dimension exercised by this PoC.
/B failsIf /B fails, first check whether libcurl actually reused the connection.
The verbose output should contain:
Re-using existing https: connection
If this line does not appear, the vulnerability condition has not been demonstrated.
/A and /B appear on different TLS connectionsThe server should show:
Connection #X -> /A
Connection #X -> /B
If instead you see:
Connection #X -> /A
Connection #Y -> /B
then the second request created a new TLS connection and the intended condition was not reproduced.
Check that:
CURL_LOCK_DATA_CONNECT is shared.CURLOPT_FORBID_REUSE is not enabled.CURLOPT_FRESH_CONNECT is not enabled.Connection: keep-alive.The client private key must be generated using:
correct-password
The PoC then intentionally uses:
WRONG-PASSWORD
Do not change the password embedded in the private-key generation command unless the corresponding PoC value is also changed.
This repository is intended for controlled security research and vulnerability reproduction.
The PoC is designed to operate against a locally hosted test server:
127.0.0.1:8443
Do not use the PoC against systems without authorization.
Private keys and generated certificates should remain outside version control.
The repository should contain only:
certs/.gitkeep
rather than generated private keys.
PoC design, laboratory methodology, implementation, and technical analysis were developed by AliReza with assistance from OpenAI ChatGPT (GPT-5.6 Luna).
Human verification, execution, testing, and reproduction were performed by the repository author.
This repository is provided for security research, vulnerability analysis, and educational purposes.
Use it only in systems and environments for which you have explicit authorization.