HexBrain

curl · Report #3650443

no_proxy IDN mismatch: Unicode hostnames bypass proxy exclusion list

Informativestatus
15upvotes
7 Apr 2026disclosed

How it was found

Summary

Unicode (IDN) hostnames in no_proxy are never converted to punycode before comparison, so they never match the request hostname (which curl has already converted to punycode). A user who types no_proxy="bücher.de" and requests http://bücher.de/ expects the proxy to be bypassed. Instead curl converts the URL to xn--bcher-kva.de, compares it raw against bücher.de, fails to match, and sends the request through the proxy. Same string typed by the user in both places, two different internal representations. This is the same class of bug as CVE-2022-42916 and CVE-2022-43551 (HSTS bypass via IDN confusion).

LANG=C.UTF-8 http_proxy=http://127.0.0.1:18877 no_proxy="bücher.de" ./curl -v "http://bücher.de/"
# Output: "Failed to connect to xn--bcher-kva.de port 80 via 127.0.0.1"
# curl tried the proxy despite no_proxy matching the URL hostname

Affected version

curl 8.20.0-DEV (Darwin) libcurl/8.20.0-DEV OpenSSL/3.6.1 zlib/1.2.12 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 nghttp2/1.68.0
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy IDN IPv6 Largefile libz SSL threadsafe TLS-SRP UnixSockets zstd
  • Git HEAD 38b626e790. Also confirmed on 8.19.0 stable. Requires IDN support (libidn2).

Steps To Reproduce

  1. Start a listener: nc -l 18877

  2. In another terminal:

LANG=C.UTF-8 \
http_proxy=http://127.0.0.1:18877 \
no_proxy="bücher.de" \
./curl -v "http://bücher.de/"
  1. The netcat listener receives the request. Verbose output shows:
* Uses proxy env variable no_proxy == 'bücher.de'
* Uses proxy env variable http_proxy == 'http://127.0.0.1:18877'
*   Trying 127.0.0.1:18877...
* Failed to connect to xn--bcher-kva.de port 80 via 127.0.0.1

The request should go direct (bypassing the proxy) because bücher.de is in no_proxy. Instead curl converts the URL hostname to punycode (xn--bcher-kva.de) before calling Curl_check_noproxy() at lib/noproxy.c:188, but never converts the no_proxy entries. match_host() at line 124 compares raw bücher.de against xn--bcher-kva.de and fails.

Impact

Traffic the user explicitly excluded from the proxy via no_proxy is sent through the proxy, leaking request URLs, headers, and bodies to the proxy operator

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