Published: June 2024

Overview

CVE-2024-37891 details a subtle but important security issue in urllib3, a popular and user-friendly HTTP client library for Python. This vulnerability affects applications that manually set the Proxy-Authorization header without using urllib3's built-in proxy support, especially when automatic HTTP redirects are involved.

Read on to learn how this bug can unintentionally leak sensitive information to untrusted domains, the circumstances required for exploitation, code examples, and how you can defend your projects today.

What’s the Issue?

In normal operation, you use the Proxy-Authorization HTTP header to send credentials (like username:password encoded in base64) to a proxy server, not to the target server. When urllib3's ProxyManager is in use, it carefully ensures this header goes only to the intended proxy.

However, if a developer manually adds the Proxy-Authorization header to a request without using ProxyManager (a rare, unintended use case), and if the request has redirects enabled, the header may be sent to a different server during a subsequent HTTP redirect—even across origins. As a result, secrets intended for an internal proxy might be leaked to a malicious website.

To trigger CVE-2024-37891, all the following are required

1. The Proxy-Authorization header is manually set in the request without using urllib3’s ProxyManager.

The proxy or server redirects the request to an attacker-controlled origin.

Practical risk: This is an unlikely combination for most users. But if it happens in your app, it could be a dangerous leak.

Here is a vulnerable usage pattern in Python with urllib3

import urllib3
import base64

# Manually encode credentials for the proxy authorization header
credentials = base64.b64encode(b"user:super_secret_password").decode("ascii")
headers = {
    "Proxy-Authorization": f"Basic {credentials}"
}

http = urllib3.PoolManager()

# Making a request without ProxyManager but setting Proxy-Authorization
resp = http.request(
    "GET",
    "http://trusted-internal-service.local/api/data";,
    headers=headers
    # redirects=True is default
)

print(resp.status, resp.data)

If this endpoint redirects (e.g., with a 302) to http://malicious-website.com/, your credentials will be sent as part of the repeated request to the new, possibly malicious, destination.

Step-by-step exploit scenario

1. Victim’s code sends a request with Proxy-Authorization header to a trusted service (not using ProxyManager).
2. Attacker controls the redirect, or the proxy/target responds with a redirect (HTTP 30x) to another origin, like http://evil-attacker.com/capture.
3. urllib3, by default, follows redirects and copies all headers, including Proxy-Authorization, to the new request.

Attacker’s server receives the credentials in the HTTP headers, now able to steal them.

Note: This generally won’t happen over HTTPS origins due to stricter browser and library rules, but it’s possible with HTTP endpoints or incorrect server configurations.

How Has urllib3 Changed?

Since 1.26.19 and 2.2.2, urllib3 will automatically strip the Proxy-Authorization header on cross-origin redirects. This prevents this header from ever reaching any untrusted site, even in the rare misconfigured case described here.

This is the safest choice.

- See urllib3 upgrades

Using ProxyManager

import urllib3

proxy_url = "http://username:super_secret_password@trusted-proxy.local:808";
http = urllib3.ProxyManager(proxy_url)

# Now Proxy-Authorization is handled internally and safely
resp = http.request("GET", "http://example.com/api";, timeout=5.)
print(resp.status)

Disabling redirects

import urllib3
import base64

headers = {
    "Proxy-Authorization": "Basic " + base64.b64encode(b"user:super_secret_password").decode("ascii")
}

http = urllib3.PoolManager()

# SAFE: disables automatic redirects
resp = http.request(
    "GET",
    "http://trusted-service.local/api/data";,
    headers=headers,
    redirect=False
)

References

- Official urllib3 CVE advisory
- CVE-2024-37891 at NVD
- urllib3 changelog
- How Python urllib3 Handles Proxies

Conclusion

CVE-2024-37891 highlights that even mainstream, robust libraries like urllib3 can have corner-case vulnerabilities. While the risk is low for most users, you should always update your dependencies promptly and use library features as intended. If your code manually sets sensitive headers, especially for authentication, always verify their behavior with redirects and cross-origin requests.

Take action: Patch urllib3, review your header usage, and trust built-in mechanisms for proxy support.

Stay secure!
*This post is brought to you exclusively for developers seeking to understand the "why," not just the fix.*

Timeline

Published on: 06/17/2024 20:15:13 UTC
Last modified on: 06/20/2024 12:44:22 UTC