curl · Report #3595753
Connection Reuse Ignores OAuth Bearer Token Mismatch
How it was found
Summary:
The connection pool reuse function url_match_conn() in lib/url.c checks oauth_bearer in its credential match block — but only for protocols marked as requiring per-connection credentials. For HTTP, OAuth bearer is passed as a header, not a protocol-level credential. If a libcurl application reuses an easy handle to connect to two different API endpoints using different bearer tokens without setting CURLOPT_FRESH_CONNECT=1, the connection pool may serve the second request on the first connection, attaching the WRONG bearer token. This is closely related to the class of bugs that produced CVE-2022-22576 (OAuth2 bearer bypass in connection reuse).
Affected version
Identified in current master
Steps To Reproduce:
Multi-tenant API server using a shared libcurl instance (common in thread pools, connection-pooling proxies, Go/Python/Java bindings):
-
User attacker1 authenticates → curl makes HTTP request with CURLOPT_XOAUTH2_BEARER = token_attacker1 → TCP connection opened to api.example.com:443 → conn->oauth_bearer = token_alice → Connection returned to pool (keep-alive)
-
User attacker2 makes a request to the SAME host → curl sets CURLOPT_XOAUTH2_BEARER = token_attacker2 on easy handle → url_match_conn() finds Alice's connection in pool → oauth_bearer check is SKIPPED (PROTOPT_CREDSPERREQUEST gate) → attacker2's connection is reused → lib/http.c builds: Authorization: Bearer token_alice ← BUG → API server receives attacker2's request authenticated as Alice → attacker2 receives attacker1's data / can take attacker2's actions
python3 poc_bearer_reuse.py
Impact
impact:
Requests to different APIs on the same host may share a persistent HTTP connection, causing the wrong OAuth bearer token to be used. In multi-tenant applications (serving different users from the same libcurl instance) this can lead to cross-account credential leakage and unauthorized API access.
recommended
Set CURLOPT_FRESH_CONNECT=1 when switching bearer tokens between requests on the same host. Application-level fix: call curl_easy_reset() or create a new easy handle per token boundary. Structural fix: ensure oauth_bearer changes force connection pool expiry for HTTP connections regardless of per-connection credential flag.