CVE-2026-7168: cross-proxy Digest auth state leak
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 HackerOneReport Stats
- Report ID: 3697719
- State: Closed
- Substate: resolved