HexBrain

curl · Report #3972293

43: HTTP proxy CONNECT header chooses the `-OJ` filename after a redirect

Informativestatus
6upvotes
7 Sept 2026disclosed

How it was found

Summary

When curl follows an HTTPS redirect through an HTTP CONNECT proxy, a stale response code lets the proxy's CONNECT response Content-Disposition header control the local output filename. With --location --remote-name --remote-header-name, the proxy-provided filename is used for the TLS-verified origin's response body, crossing the CONNECT/origin trust boundary.

Root cause

Curl_req_soft_reset() in lib/request.c does not clear data->info.httpcode. After the first origin's 302 response, this stale value persists while curl establishes a new CONNECT tunnel for the redirect target.

single_header() in lib/cf-h1-proxy.c delivers CONNECT response headers to the header callback with CLIENTWRITE_CONNECT, but this provenance bit is internal to the writer chain — it never reaches CURLOPT_HEADERFUNCTION. The stale httpcode is only cleared when the tunnel reaches H1_TUNNEL_ESTABLISHED, after headers have already been delivered.

tool_header_cb() in src/tool_cb_hdr.c calls CURLINFO_RESPONSE_CODE, sees the stale 302, and passes the proxy's Content-Disposition header to content_disposition(), which opens the output file under the proxy-chosen name and disables further filename handling. The real origin's Content-Disposition header arrives later but is ignored.

Steps to reproduce

The PoC uses two certificate-verified HTTPS origins and an HTTP/1.1 CONNECT proxy, all on loopback. Origin 1 returns a 302 redirect to Origin 2. The proxy injects Content-Disposition: attachment; filename="proxy-chosen.bin" in the CONNECT response for Origin 2. Origin 2 returns Content-Disposition: attachment; filename="origin-name.bin" with a body.

Expected vs actual output

Expected: All three cases produce origin-name.bin — the filename from the TLS-verified origin.

Actual: The redirect case produces proxy-chosen.bin containing the authenticated origin's body. The two controls correctly produce origin-name.bin. This shows the proxy's CONNECT response header overwrites the filename only when a stale 3xx code is present from a prior redirect.

Suggested fix

Prevent CONNECT-provenance headers from participating in --remote-header-name processing. The strongest fix would propagate header provenance to the command-line callback so it can distinguish CONNECT headers from origin headers. Alternatively, clearing data->info.httpcode before CONNECT headers are delivered (rather than after, in H1_TUNNEL_ESTABLISHED) would close this specific trigger.

Impact

A malicious HTTP proxy (or an on-path attacker against the plaintext proxy hop) can choose the local basename for data returned by an authenticated HTTPS origin. The attacker cannot modify the TLS-protected response body, but controls the name under which curl stores it.

The filename is subject to curl's basename sanitization. Existing files are preserved by default; --clobber permits replacement. Using --suppress-connect-headers mitigates the issue.

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