Improper enforcement of CURLOPT_SOCKS5_AUTH due to missing reuse key validation in libcurl

Disclosed: 2026-04-07 06:24:02 By cutiapretaa To curl
Low
Vulnerability Details
# detail: - `lib/setopt.c:1048-1051` - `CURLOPT_SOCKS5_AUTH` is stored into `data->set.socks5auth` - `lib/socks.c:597-641` (`socks5_req0_init`) - fresh SOCKS5 handshake reads `data->set.socks5auth`, if BASIC is not allowed, it clears `sx->proxy_user` at 618-620, so username/password auth is not even offered - `lib/socks.c:665-688` (`socks5_check_resp0`) - on a fresh connection, if the proxy selects BASIC while BASIC is disabled, or if no acceptable method exists, the connection fails - `lib/urldata.h:345-351` - struct `proxy_info` stores host / port / proxytype / user / passwd, but no SOCKS5 auth-method policy - `lib/url.c:578-590` (`proxy_info_matches`) - reuse match compares only proxy type, port, host, proxy user, and proxy password - `lib/url.c:879-913` (`url_match_proxy_use`) - SOCKS proxy reuse uses `proxy_info_matches()` - `lib/url.c:1207-1229` (`url_match_conn`) - connection reuse is accepted if that proxy match succeeds - `lib/url.c:2965-2998` (`url_conn_reuse_adjust`) - on reuse, curl may refresh proxy credentials, but it does not compare or refresh the SOCKS5 auth-method policy so the invariant break is: fresh SOCKS5 connections enforce `CURLOPT_SOCKS5_AUTH`, but connection reuse does not include that policy in the reuse key ## docs: this is not an API misuse case changing options between transfers on the same easy handle is a documented libcurl usage pattern, and connection reuse is the default behavior unless the application forces a fresh connection, if curl wanted `CURLOPT_SOCKS5_AUTH` to be “new connection only,” its docs should say so the same way they do for `CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256`, they do not, that makes silent reuse under a stricter later auth policy a bug, not intended behavior ( ref: https://curl.se/libcurl/c/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.html, https://curl.se/libcurl/c/curl_easy_setopt.html , https://curl.se/libcurl/c/CURLOPT_SOCKS5_AUTH.html , https://curl.se/libcurl/c/CURLOPT_FRESH_CONNECT.html ) # exploit: 1. create a normal easy handle 2. set: - `CURLOPT_URL = http://127.0.0.1:18081/` - `CURLOPT_PROXY = socks5://127.0.0.1:19081` - `CURLOPT_PROXYUSERPWD = "user:pass"` - `CURLOPT_SOCKS5_AUTH = CURLAUTH_BASIC` 3. perform transfer #1 - the SOCKS5 proxy requires username/password, so the SOCKS handshake succeeds and the connection stays alive for reuse 4. on the same easy handle, change only: - `CURLOPT_SOCKS5_AUTH = CURLAUTH_NONE` 5. perform transfer #2 to the same proxy/destination - libcurl reuses the existing connection because the reuse key ignores the SOCKS5 auth-method policy 6. no new SOCKS5 handshake happens, so the request succeeds over the already-authenticated tunnel 7. do the same “NONE only” transfer on a fresh easy handle - it fails with `CURLE_PROXY` / error 97, because `CURLAUTH_NONE` means only no-auth is allowed and the proxy requires username/password # poc: i ran a local setup: - SOCKS5 proxy on 127.0.0.1:19081 that requires username/password - HTTP origin on 127.0.0.1:18081 - C poc using the uploaded libcurl build **what happened:** 1. transfer #1 on one easy handle with - `CURLOPT_PROXY = socks5://127.0.0.1:19081` - `CURLOPT_PROXYUSERPWD = "user:pass"` - `CURLOPT_SOCKS5_AUTH = CURLAUTH_BASIC` - result: success 2. transfer #2 on the same easy handle, changing only - `CURLOPT_SOCKS5_AUTH = CURLAUTH_NONE` - result: success - log: reusing existing http: connection with proxy 127.0.0.1 3. transfer #3 on a fresh easy handle with - `CURLOPT_SOCKS5_AUTH = CURLAUTH_NONE` - result: failure - error: 97 (proxy handshake error) - log: no authentication method was acceptable **server-side logs:** - first connection offered methods `[0, 2]` and completed username/password auth - second transfer reused the same SOCKS connection and sent the HTTP request with no new SOCKS handshake - fresh third attempt offered only method `[0]` and the proxy rejected it ## Impact # impacts: - SOCKS5 proxy-auth policy bypass - a transfer that is explicitly configured with `CURLOPT_SOCKS5_AUTH = CURLAUTH_NONE` can succeed when it should fail - the second transfer silently continues over an already-authenticated SOCKS5 tunnel created under a less restrictive earlier policy - applications can believe they enforced “no proxy authentication” for a later transfer, while libcurl actually keeps using a previously BASIC-authenticated proxy connection
Actions
View on HackerOne
Report Stats
  • Report ID: 3650435
  • State: Closed
  • Substate: informative
Share this report