CVE-2026-7168: cross-proxy Digest auth state leak

Disclosed: 2026-04-29 07:15:46 By xkilua To curl
Medium
Vulnerability Details
## Summary: On libcurl 8.19.0, Proxy Digest state learned from proxyA survives an independent transfer boundary on a reused easy handle and is emitted preemptively to proxyB when the proxy is changed. In the attached C PoC, the first CONNECT to proxyB carries `Proxy-Authorization: Digest ...` built from `proxyArealm` / `proxyAnonce` instead of starting unauthenticated and waiting for a challenge from proxyB. The leaked header is replayable to proxyA for the same CONNECT authority and is accepted with `200`, while replay to a different authority is rejected with `407`. A fresh easy handle and `curl_easy_reset()` both suppress the leak. I also confirmed that changing proxy credentials before the second transfer still causes libcurl to build the first proxyB CONNECT from stale proxyA Digest challenge state, and that a proxyB which only advertises Basic still receives stale Digest on the first CONNECT. ## Affected version Reproduced on Linux x86_64 with: `curl 8.19.0 (x86_64-pc-linux-gnu) libcurl/8.19.0 OpenSSL/3.5.5 zlib/1.3.1 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.5 libssh2/1.11.1 nghttp2/1.68.1 ngtcp2/1.21.0 nghttp3/1.15.0 librtmp/2.3 mit-krb5/1.22.1 OpenLDAP/2.6.10` The attached C PoC also prints the runtime version string: `curl_version=libcurl/8.19.0 ...` ## Steps To Reproduce: 1. Extract the attached `curl_proxy_connect_bundle.zip`. 2. Start the local proxy harness: `python3 proxy_digest_connect_servers.py proxy_events.jsonl` 3. Compile the attached C PoC against the libcurl 8.19.0 build you want to test. On my system I used: `cc -I/home/nobcoder/curl/curl/include poc_proxy_connect_digest_reuse.c /usr/lib/x86_64-linux-gnu/libcurl.so.4 -o poc_proxy_connect_digest_reuse_c` 4. Run the PoC: `./poc_proxy_connect_digest_reuse_c` 5. Observe the base case output: - `base1_res=0 base1_code=200` - `base2_res=56 base2_code=0` - `leaked_proxy_b=Digest username="proxyuser", realm="proxyArealm", nonce="proxyAnonce", uri="victim.test:18099", ...` - `raw_first_connect_proxy_b=CONNECT victim.test:18099 HTTP/1.1 ... Proxy-Authorization: Digest username="proxyuser", realm="proxyArealm", nonce="proxyAnonce", ...` - `replay_status=200` - `replay_used_password=false` - `mismatch_status=407` 6. Observe the controls: - fresh easy handle: `fresh_first_b=(null)` - `curl_easy_reset()`: `reset_first_b=(null)` - changed credentials before proxyB: `altcreds_first_b=Digest username="otheruser", realm="proxyArealm", nonce="proxyAnonce", ...` - proxyB that only advertises Basic: `basicchallenge_first_b=Digest username="proxyuser", realm="proxyArealm", nonce="proxyAnonce", ...` 7. Inspect the attached `proxy_events.jsonl` for the full raw request log from proxyA and proxyB, including the exact first CONNECT to proxyB. Relevant source lines against release tag `curl-8_19_0`: - `lib/urldata.h:963-966` stores `proxydigest` and `authproxy` on easy-handle state - `lib/transfer.c:500-501` and `550-551` preserve proxy auth state across new transfers - `lib/http_digest.c:93-97`, `120`, and `161-162` read stale `data->state.proxydigest` and build `Proxy-Authorization: Digest ...` - `lib/http.c:809` and `lib/http_proxy.c:225-238` place proxy auth on the CONNECT path - `lib/url.c:588-595` and `904-919` show curl already separates connections by proxy identity, so this is not just wrong cross-proxy connection reuse ## Impact ## Summary: This is a cross-proxy authentication boundary break. A later attacker-controlled proxy can receive a `Proxy-Authorization: Digest` response that belongs to a different proxy’s CONNECT challenge, and in the attached C PoC that leaked header is replayable to proxyA for the same CONNECT authority and is accepted with `200` without the attacker knowing the password. A different CONNECT authority is rejected with `407`, which shows the leaked header retains real authentication value rather than being stale metadata only. Fresh easy handles and `curl_easy_reset()` do not leak the header, which ties the issue to stale easy-handle-scoped Proxy Digest state crossing proxy identity boundaries.
Actions
View on HackerOne
Report Stats
  • Report ID: 3697719
  • State: Closed
  • Substate: resolved
Share this report