CVE-2026-8927: env-set cross-proxy Digest auth state leak
What the report claims
Stale Digest proxy authentication state from a previous proxy selected via environment variables is incorrectly sent to a new proxy when reusing a libcurl easy handle.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-8927 env-set cross-proxy Digest auth state leak
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
Why this finding?
- The provided curl source only shows curl_easy_reset, which explicitly clears proxy auth state. The critical code — proxy resolution from environment variables (http_proxy/ALL_PROXY) and the perform-time logic that should detect a proxy change and clear stale Digest state — is not included in the source snippets.
- Deterministic checks confirm the relevant symbols exist but do not show their usage in the proxy resolution or auth state management paths.
- The report describes a plausible variant of a known CVE (CVE-2026-7168) where the explicit CURLOPT_PROXY path was fixed but the environment-variable path may have been missed. This pattern is credible but cannot be confirmed without the missing code.
- If the environment-variable proxy resolution path lacks the cleanup that the CURLOPT_PROXY path has, this would be a valid security issue (cross-proxy credential leak). If the cleanup is shared, the report is invalid.
What still needs checking
- The function that resolves the effective proxy from environment variables (likely in lib/url.c or lib/easy.c) and the code in curl_easy_perform or the HTTP proxy connect logic that manages proxy auth state across transfers on a reused handle.
How this analysis ran 5 claims · 1 citations verified
Checked against curl master on 2026-05-18 (240408a72542). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the detailed report and PoC. The issue you describe — a missing proxy-auth cleanup when the effective proxy changes via environment variables — is a plausible variant of CVE-2026-7168. However, the provided source snippets only show curl_easy_reset (an explicit reset), not the proxy resolution logic or the perform-time state management that handles implicit proxy changes. To evaluate this, we need to see the code that resolves proxies from http_proxy/ALL_PROXY and the path in curl_easy_perform (or the HTTP proxy connect logic) that should clear data->state.proxydigest and data->state.authproxy when the effective proxy changes between transfers on a reused handle. Could you point to the specific functions/files where this resolution and cleanup should occur?
Inspect all claims and source code 5 claims · 1 code excerpts
Report claims
When the effective proxy changes through environment variables such as http_proxy or ALL_PROXY, the previous proxy Digest/auth state is not cleared before the second transfer on a reused easy handle.
Read the reasoning
The central claim is that changing the effective proxy via environment variables does not clear proxy Digest/auth state. The provided source snippet only shows curl_easy_reset (an explicit reset API), not the proxy resolution logic or the perform-time state management that would handle implicit proxy changes from environment variables. Without the code that resolves proxies from environment variables and manages auth state across transfers, the claim cannot be verified or contradicted from the provided material.
The issue occurs in the proxy authentication state management of libcurl's easy handle when the proxy is resolved from environment variables.
Read the reasoning
The claim identifies the general area (proxy authentication state management) but the specific code location where environment-variable proxy resolution fails to clear state is not shown in the provided source. The deterministic checks only confirm the symbols exist, not the bug location.
Changing the environment variable from proxyA to proxyB while reusing the same easy handle causes the stale Proxy-Authorization header to be sent to the new proxy.
Read the reasoning
The trigger description is plausible but the code path that processes environment variable changes between transfers on a reused handle is not provided. Cannot confirm whether the stale header is actually sent.
A malicious second proxy can receive a Proxy-Authorization: Digest header generated from the previous proxy's Digest challenge state, enabling replay attacks.
Read the reasoning
Impact claim depends on C1 being true. Since C1 is not verifiable from provided code, impact cannot be assessed.
The PoC starts two local proxy servers, first uses http_proxy to authenticate with proxyA, then changes http_proxy to point to proxyB and reuses the same easy handle, causing proxyB to receive a stale Proxy-Authorization header containing realm and nonce from proxyA.
Read the reasoning
Reproduction steps describe a test scenario. The actual curl code path that would exhibit the behavior is not in the provided source.
Read the original report (excerpt)
Source code
| 1084 | void curl_easy_reset(CURL *curl) |
| C1: Only curl_easy_reset is shown, which explicitly clears authproxy and calls Curl_http_auth_cleanup_digest. The proxy resolution logic for environment variables (http_proxy/ALL_PROXY) and the code path that should clear proxy auth state when the effective proxy changes during curl_easy_perform are not provided. | |
| 1085 | { |
| 1086 | struct Curl_easy *data = curl; |
| 1087 | if(!GOOD_EASY_HANDLE(data)) |
| 1088 | return; |
| 1089 | |
| 1090 | Curl_req_hard_reset(&data->req, data); |
| 1091 | Curl_hash_clean(&data->meta_hash); |
| 1092 | |
| 1093 | /* clear all meta data */ |
| 1094 | Curl_meta_reset(data); |
| 1095 | /* zero out UserDefined data: */ |
| 1096 | Curl_freeset(data); |
| 1097 | memset(&data->set, 0, sizeof(struct UserDefined)); |
| 1098 | Curl_init_userdefined(data); |
| 1099 | |
| 1100 | /* zero out Progress data: */ |
| 1101 | memset(&data->progress, 0, sizeof(struct Progress)); |
| 1102 | |
| 1103 | /* zero out PureInfo data: */ |
| 1104 | Curl_initinfo(data); |
| 1105 | |
| 1106 | data->progress.hide = TRUE; |
| 1107 | data->state.current_speed = -1; /* init to negative == impossible */ |
| 1108 | data->state.recent_conn_id = -1; /* clear remembered connection id */ |
| 1109 | |
| 1110 | /* zero out authentication data: */ |
| 1111 | memset(&data->state.authhost, 0, sizeof(struct auth)); |
| 1112 | memset(&data->state.authproxy, 0, sizeof(struct auth)); |
| 1113 | |
| 1114 | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) |
| 1115 | Curl_http_auth_cleanup_digest(data); |
| 1116 | #endif |
| 1117 | data->master_mid = UINT32_MAX; |
| 1118 | } |