CVE-2026-8932 の概念実証(PoC)再現です。これは libcurl の接続再利用における mTLS 設定の照合が不完全である問題です。
CVE-2026-8932 は、リクエスト間で相互 TLS(mTLS)設定が変更された際の libcurl の接続再利用ロジックに影響します。
脆弱な条件下では、mTLS 関連の設定オプションが変更され、その接続の再利用を防ぐべき状況であっても、libcurl が既存の TLS 接続を再利用してしまう可能性があります。
この PoC は、同じクライアント証明書と秘密鍵を使用しながら、2 つのリクエスト間で秘密鍵のパスワードを変更することで、この問題を実証します。
最初のリクエストでは正しいパスワードを使用します:
correct-password
2 番目のリクエストは意図的に無効なパスワードを使用しています:
WRONG-PASSWORD
libcurl が既存の TLS 接続を誤って再利用した場合、2 回目のリクエストでは新たな TLS ハンドシェイクが不要になります。その結果、無効な秘密鍵パスワードは新しい TLS 接続を確立するために必要とされず、リクエストは成功します。
したがって、この PoC は CVE-2026-8932 に関連する接続再利用の条件を実証しています。
| プロパティ | 値 |
|---|---|
| CVE | CVE-2026-8932 |
| コンポーネント | libcurl |
| 脆弱性の種類 | 不完全な mTLS 設定マッチング |
| CWE | CWE-305 — 一次的な弱点による認証バイパス |
| 影響範囲 | TLS 接続の再利用 |
| プロトコル | HTTPS / mTLS |
| クライアント | libcurl API |
| curl CLI | 影響なし |
| PoC の範囲 | ローカル実験環境 |
この脆弱性は、バッファオーバーフローや use-after-free のような従来のメモリ安全性の脆弱性ではなく、ロジック/設定マッチングの問題です。
libcurl は接続情報を保持しており、後続のリクエストが接続の設定と互換性があると見なされた場合、既存の接続を再利用できます。
そのため、TLS 接続では、再利用する前に接続設定を慎重に比較する必要があります。
脆弱な実装では、接続マッチングロジックにすべての関連する mTLS 設定フィールドが含まれていませんでした。
影響を受ける設定には以下が含まれます:
cert_type
key
key_type
key_passwd
key_blob
このPoCで使用される重要な設定は次のとおりです:
key_passwd
PoCは、暗号化されたクライアント秘密鍵と正しいパスワードを使用してTLS接続を確立します:
correct-password
次に、同じ証明書と鍵を使用して別のリクエストを実行しますが、パスワードを次のように変更します:
WRONG-PASSWORD
脆弱な接続マッチング実装では、既存の接続が再利用可能と見なされる可能性があります。
このテストは 2 つの HTTP リクエストで構成されます。
最初のリクエストで TLS 接続を確立します:
URL:
https://server.test:8443/A
Client certificate:
client.crt
Private key:
client.key
Key password:
correct-password
TLS 接続は、サーバーが HTTP keep-alive をサポートしているため、永続的に維持されます。
2 番目のリクエストでは、同じ証明書と秘密鍵を使用しますが、鍵のパスワードを変更します:
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
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
サーバーの秘密鍵を生成します:
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
そのため、2回目のリクエストでは、変更された鍵設定を使用して別の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 接続を介して受信されました。
サーバーはまた、同じクライアント ID を報告します:
Client-A
for both requests.
PoC で使用される秘密鍵は暗号化されています。
最初のリクエストでは次を使用します:
correct-password
libcurl/OpenSSL が秘密鍵にアクセスし、TLS 接続を確立できるようにします。
2 番目のリクエストは、設定を次のように変更します:
WRONG-PASSWORD
新しいTLS接続を確立する必要がある場合、libcurlは暗号化された秘密鍵を再度処理する必要があり、誤ったパスワードは操作を失敗させるはずです。
しかし、既存のTLS接続が再利用される場合、TLSセッションはすでに確立されています。
/Bに対して新しいクライアント認証操作は必要ありません。
概念的には: