CVE-2026-8932 的概念验证复现,该漏洞是 libcurl 连接复用中 mTLS 配置匹配不完整的问题。
CVE-2026-8932 影响 libcurl 的连接复用逻辑,当请求之间的双向 TLS(mTLS)配置发生变化时会出现该问题。
在存在漏洞的条件下,即使某个与 mTLS 相关的配置选项已经改变、本应阻止该连接被复用,libcurl 仍可能复用现有的 TLS 连接。
本 PoC 通过在两个请求之间更改私钥密码、同时使用相同的客户端证书和私钥来演示该问题。
第一个请求使用正确的密码:
correct-password
第二个请求故意使用了无效密码:
WRONG-PASSWORD
如果 libcurl 错误地复用了现有的 TLS 连接,则第二个请求不需要再进行一次 TLS 握手。因此,无效的私钥密码永远不会被用于建立新的 TLS 连接,请求便会成功。
因此,该 PoC 演示了与 CVE-2026-8932 相关的连接复用条件。
| 属性 | 值 |
|---|---|
| CVE | CVE-2026-8932 |
| 组件 | libcurl |
| 漏洞类型 | 不完整的 mTLS 配置匹配 |
| CWE | CWE-305 — 通过主要弱点绕过身份验证 |
| 受影响区域 | TLS 连接复用 |
| 协议 | HTTPS / mTLS |
| 客户端 | libcurl API |
| curl CLI | 不受影响 |
| PoC 范围 | 本地实验室环境 |
该漏洞是一个逻辑/配置匹配问题,而非传统的内存安全漏洞,例如缓冲区溢出或释放后使用。
libcurl 维护连接信息,并且当后续请求被认为与连接的配置兼容时,可以复用现有连接。
因此,对于 TLS 连接,在复用之前必须仔细比较连接配置。
存在漏洞的实现未将全部相关的 mTLS 配置字段纳入连接匹配逻辑。
受影响的设置包括:
cert_type
key
key_type
key_passwd
key_blob
此 PoC 使用的重要设置是:
key_passwd
PoC 使用加密的客户端私钥和正确的密码建立 TLS 连接:
correct-password
然后它使用相同的证书和密钥执行另一个请求,但将密码更改为:
WRONG-PASSWORD
一个存在漏洞的连接匹配实现可能仍会认为现有连接可复用。
该测试由两个 HTTP 请求组成。
第一个请求建立 TLS 连接:
URL:
https://server.test:8443/A
Client certificate:
client.crt
Private key:
client.key
Key password:
correct-password
TLS 连接保持持久,因为服务器支持 HTTP keep-alive。
第二个请求使用相同的证书和私钥,但更改了密钥密码:
URL:
https://server.test:8443/B
Client certificate:
client.crt
Private key:
client.key
Key password:
WRONG-PASSWORD
如果 libcurl 创建新的 TLS 连接,使用错误密码加载加密私钥应当失败。
然而,如果 libcurl 错误地复用了现有连接,则不需要新的 TLS 握手。
因此,无效密码并不能阻止 HTTP 请求成功。
实验室环境由以下部分组成:
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)
关键观察是 /A 和 /B 必须通过同一条 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
certs/ 目录在仓库中被有意保持为空。证书和私钥应在本地生成。
该 PoC 已在以下环境中测试:
OS:
Debian GNU/Linux 13 (trixie)
Architecture:
x86_64
libcurl:
8.14.1
OpenSSL:
3.5.7
GCC:
14.2.0
用于复现的易受攻击的 libcurl 安装版本为:
libcurl/8.14.1
验证已安装的版本:
curl --version
和:
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
生成服务器私钥:
openssl genrsa -out server.key 2048
生成 CSR:
openssl req -new \
-key server.key \
-subj "/CN=server.test" \
-out server.csr
创建证书扩展:
printf "subjectAltName=DNS:server.test\nextendedKeyUsage=serverAuth\n" \
> server.ext
使用测试 CA 对证书进行签名:
openssl x509 -req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out server.crt \
-days 3650 \
-sha256 \
-extfile server.ext
生成客户端私钥:
openssl genrsa -out client.key.tmp 2048
生成 CSR:
openssl req -new \
-key client.key.tmp \
-subj "/CN=Client-A" \
-out client.csr
将私钥转换为加密的 PKCS#8 格式:
openssl pkcs8 \
-topk8 \
-in client.key.tmp \
-out client.key \
-v2 aes-256-cbc \
-passout pass:correct-password
生成的私钥使用以下方式加密:
correct-password
创建客户端证书扩展:
printf "extendedKeyUsage=clientAuth\n" \
> client.ext
为客户端证书签名:
openssl x509 -req \
-in client.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out client.crt \
-days 3650 \
-sha256 \
-extfile client.ext
此时所需文件应已存在:
certs/
├── ca.crt
├── ca.key
├── client.crt
├── client.key
├── server.crt
└── server.key
进入服务器目录:
cd ~/cve-2026-8932-lab/server
启动服务器:
python3 server.py
服务器监听于:
0.0.0.0:8443
需要客户端证书认证。
服务器还会保持 HTTP/1.1 连接存活,以便 libcurl 可以复用 TLS 连接。
打开另一个终端:
cd ~/cve-2026-8932-lab/poc
编译:
gcc -Wall -Wextra -O0 -g \
poc.c \
$(pkg-config --cflags --libs libcurl) \
-o poc
执行:
./poc
该 PoC 使用:
Client certificate : client.crt
Private key : client.key
Request A password : correct-password
Request B password : WRONG-PASSWORD
最重要的客户端证据是:
[+] 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
请求随后会收到:
< HTTP/1.1 200 OK
以及 PoC 报告:
[!!!] B REQUEST SUCCEEDED
这很重要,因为 /B 使用了一个故意无效的私钥密码。
关键观察点并不只是 /B 成功执行。
关键证据在于 libcurl 明确报告:
Re-using existing https: connection
因此,第二个请求无需使用修改后的密钥配置再次执行 TLS 握手。
服务器输出为连接复用提供了独立确认。
相关输出为:
[+] 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
重要观察是:
/A -> Connection #2
/B -> Connection #2
两个 HTTP 请求因此是通过同一条 TLS 连接接收的。
服务器还报告了相同的客户端身份:
Client-A
用于这两个请求。
PoC 使用的私钥是加密的。
第一个请求使用:
correct-password
这允许 libcurl/OpenSSL 访问私钥并建立 TLS 连接。
第二个请求将配置更改为:
WRONG-PASSWORD
如果必须建立新的 TLS 连接,libcurl 将需要再次处理加密的私钥,而错误的密码应导致该操作失败。
然而,当复用现有的 TLS 连接时,TLS 会话已经建立。
对于 /B,不需要进行新的客户端认证操作。
概念上:
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
本 PoC 演示的是连接复用,而非 TLS 会话恢复。
这两种机制是不同的。
一个已建立的 TLS 连接保持打开状态,并被用于另一个 HTTP 请求:
TLS connection #2
|
+-- GET /A
|
+-- GET /B
无需进行第二次 TLS 握手。
会创建一个新的 TCP/TLS 连接,但会使用先前连接中的加密会话信息来缩短新的 TLS 握手过程。
这并不是本 PoC 所依赖的机制。
本 PoC 有意共享:
CURL_LOCK_DATA_CONNECT
between the two easy handles while not sharing:
CURL_LOCK_DATA_SSL_SESSION
这样可以让演示聚焦于连接复用。
该仓库包含成功复现时捕获的输出。