HexBrain

curl · Report #3673277

libcurl stale CURLOPT_AUTOREFERER leaks a previous request URL to a different origin on a reused easy handle

Informativestatus
lowseverity
22upvotes
17 Apr 2026disclosed

How it was found

Summary:

libcurl keeps a stale data->state.referer after an HTTP redirect when CURLOPT_AUTOREFERER is enabled. Curl_http_follow() stores the previous URL into data->state.referer at lib/http.c:1166-1189, and later requests reuse that value when building Referer: at lib/http.c:2954-2957. In my local checkout, lib/transfer.c:495-503 resets follow/auth state but does not reset data->state.referer. As a result, after one redirecting transfer completes, a subsequent unrelated request on the same easy handle can send a stale Referer: header to a different origin. I reproduced this on curl 8.19.0 / libcurl 8.19.0 on x86_64 Linux, and included controls showing the leak disappears when the second request uses a new easy handle or when CURLOPT_AUTOREFERER is disabled. An attachment with the PoC source, server script, and captured output is included.

Affected version

Reproduced on: curl 8.19.0 (x86_64-pc-linux-gnu) libcurl/8.19.0 OpenSSL/3.5.5 zlib/1.3.1 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.5 libssh2/1.11.1 nghttp2/1.68.1 ngtcp2/1.21.0 nghttp3/1.15.0 librtmp/2.3 mit-krb5/1.22.1 OpenLDAP/2.6.10 Platform: Linux x86_64

Steps To Reproduce:

  1. Start the attached local server: python3 -u stale_autoreferer_server.py 18084
  2. Build the attached PoC: cc poc_stale_autoreferer.c -o poc_stale_autoreferer $(curl-config --cflags --libs)
  3. Run the PoC: ./poc_stale_autoreferer
  4. Observe the base case output: req1=0 effective=http://b.test:18084/final referer=http://a.test:18084/start?token=SECRET123 req2=0 effective=http://attacker.test:18084/check referer=http://a.test:18084/start?token=SECRET123
  5. Observe control 1: with a new easy handle for request 2, attacker.test receives referer=(null)
  6. Observe control 2: with CURLOPT_AUTOREFERER disabled, the stale Referer: is not sent on request 2
  7. Observe the attached server log for the base case: {"host": "a.test:18084", "path": "/start?token=SECRET123", "referer": null} {"host": "b.test:18084", "path": "/final", "referer": "http://a.test:18084/start?token=SECRET123"} {"host": "attacker.test:18084", "path": "/check", "referer": "http://a.test:18084/start?token=SECRET123"}

Impact

Summary:

This causes cross-origin disclosure of the previous transfer URL through a stale Referer: header on a later request using the same easy handle. Because the leaked URL can include query parameters, path segments, signed URLs, internal endpoint names, or other sensitive values, an attacker-controlled origin can receive data unrelated to the current request. The included controls show that the leak is specifically caused by persisted CURLOPT_AUTOREFERER state across transfers, not normal referer behavior.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original