HexBrain

curl · Report #3973127

18: Explicit IPv6 proxy zone ID silently ignored — proxy credentials sent to wrong interface

Informativestatus
16upvotes
31 Aug 2026disclosed

How it was found

Explicit IPv6 proxy zone ID silently ignored — proxy credentials sent to wrong interface

Summary

When a proxy is configured with a literal link-local IPv6 address and an explicit zone ID (e.g. http://[fe80::1%25eth0]:8080), curl parses and stores the zone correctly but never applies it to the socket. On a multi-homed host, get_scope_id() falls back to the first link-local interface it finds, which may be a different one. This causes proxy credentials (Proxy-Authorization) to be sent to an unintended network endpoint.

This is a regression introduced by commit bc40e09f63 ("lib: introduce Curl_peer", 2026-05-05). Before that refactor, zonefrom_url() in parse_proxy() placed the proxy's zone ID into conn->scope_id. The refactor deleted zonefrom_url() and moved zone storage to peer->scopeid, but conn->scope_id is now only set from CURLOPT_ADDRESS_SCOPE or the origin peer — never from the proxy peer. This regresses the fix from PR #3918 / issue #3482 ("parse_proxy: use the IPv6 zone id if given").

Affected version

curl 8.21.0, plus affected 8.22.0 prerelease/development versions. Introduced by bc40e09f63.

Root cause

  1. peer_create() correctly converts the proxy URL zone ID to proxy_peer->scopeid (lib/peer.c:146-170).

  2. Curl_str2addr() / ip2addr() creates a zero-initialized sockaddr_in6 without copying any scope ID (lib/curl_addrinfo.c:406-412).

  3. Before connect(), get_scope_id() (lib/cf-socket.c:379-417) checks:

    • conn->scope_id — set from CURLOPT_ADDRESS_SCOPE or origin peer, not the proxy peer (lib/url.c:1566-1567)
    • sa6->sin6_scope_id — always zero (see step 2)
    • First link-local interface via getifaddrs() — picks whichever comes first

    It never checks Curl_conn_get_first_peer(conn, ...)->scopeid. The proxy's explicit zone is parsed, stored, and then silently discarded.

Security impact

On a host with multiple link-local interfaces where a different interface shares the same link-local address, curl connects to the wrong endpoint and sends proxy credentials there. The reproduction below proves disclosure of HTTP Basic Proxy-Authorization to an unintended listener. The same routing error would affect HTTPS CONNECT tunneling and SOCKS proxy authentication.

The configuration is narrow (literal link-local proxy, multiple eligible interfaces, same link-local address on the wrong interface), but the user explicitly specified the correct interface via the standard zone ID syntax and curl silently ignored it.

Reproduction

  1. Build curl 8.21.0 or another affected revision with IPv6 and proxy support enabled.
  2. Prepare a host or isolated network namespace with two active IPv6 link-local interfaces.
  3. Place a separate endpoint behind each interface and assign both endpoints the same link-local address and TCP port, such as [fe80::1]:18080.
  4. Ensure the unintended interface is enumerated before the intended interface so that curl's automatic link-local scope fallback selects the unintended one.
  5. Start a simple HTTP proxy listener on each endpoint. Have each listener record incoming connections, request lines, and any Proxy-Authorization header.
  6. Configure curl to use the proxy's literal link-local IPv6 address with an explicit zone identifying the intended interface, percent-encoding the zone delimiter in the URL, for example http://[fe80::1%25intended0]:18080.
  7. Configure HTTP Basic proxy credentials, clear any proxy-bypass configuration, and request an HTTP URL through the proxy.
  8. Record the scope ID used by curl and identify which listener receives the connection.
  9. Observe the vulnerable result: curl ignores the zone from the proxy URL, falls back to the unintended interface, and sends the proxy request and credentials to that endpoint. The listener on the explicitly selected interface receives nothing.
  10. Repeat the test with the numeric interface index in the proxy URL instead of its name. Confirm that the explicit numeric zone is ignored in the same way.
  11. As a control, perform the same transfer through libcurl while setting CURLOPT_ADDRESS_SCOPE to the intended interface index.
  12. Confirm that the control connects only through the intended interface and that the unintended listener receives no credentials.
  13. Optionally repeat the vulnerable case with an HTTPS proxy tunnel or a SOCKS proxy and verify that the same routing error exposes the corresponding proxy-authentication exchange to the unintended endpoint.
  14. Confirm a fix by making socket setup prefer the selected first peer's explicit scope ID before automatic interface fallback, while preserving an explicit CURLOPT_ADDRESS_SCOPE as the highest-priority override.
  15. Repeat the original test and verify that only the listener behind the zone specified in the proxy URL receives the connection and credentials.

Expected result

An explicit IPv6 zone in a proxy URL must determine the interface used to reach that link-local proxy. Curl must not send proxy traffic or credentials through another interface.

Vulnerable result

Curl parses and stores the proxy zone but does not apply it when opening the socket. Automatic scope selection can therefore route the connection and proxy credentials to an endpoint on a different interface.

Suggested fix

In get_scope_id() (lib/cf-socket.c), after checking conn->scope_id and before the getifaddrs fallback, check the first peer's explicit scope:

if(data->conn->scope_id)
    return data->conn->scope_id;

/* Check the first peer's explicit zone (proxy or connect-to) */
{
    struct Curl_peer *peer = Curl_conn_get_first_peer(data->conn, 0);
    if(peer && peer->scopeid)
        return peer->scopeid;
}

scope_id = sa6->sin6_scope_id;
/* ... existing fallback ... */

Impact

Summary:

An attacker controlling the same link-local IPv6 address on another interface may receive traffic intended for the explicitly selected proxy, including HTTP Basic credentials, HTTPS CONNECT authentication, or SOCKS credentials. This can disclose proxy secrets and potentially allow the attacker to intercept or manipulate proxied traffic.

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