CURLOPT_HAPROXY_CLIENT_IP lacks input validation, enabling HAProxy PROXY protocol injection

Disclosed: 2026-06-26 14:34:39 By tneelc To curl
Medium
Vulnerability Details
Summary The CURLOPT_HAPROXY_CLIENT_IP option accepts an arbitrary string without validating that it is a valid IP address, and without stripping special characters such as \r\n (CRLF) or spaces. Because this value is embedded directly into the HAProxy PROXY protocol v1 header line, an attacker who can influence the value passed to this option can inject arbitrary content into the protocol stream. Unlike the documented CRLF injection risk for HTTP headers (noted in libcurl-security.md), this injection vector is not documented and not warned about, despite following the same pattern. --- Affected Versions curl / libcurl ≤ 8.21.0 (current master, commit 4ce309d968) --- Root Cause File: lib/cf-haproxy.c, lines 84–97 if(data->set.str[STRING_HAPROXY_CLIENT_IP]) { client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; client_dest_ip = client_source_ip; /* same value for both src and dst */ is_ipv6 = !Curl_is_ipv4addr(client_source_ip); } result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %d %d\r\n", is_ipv6 ? "TCP6" : "TCP4", client_source_ip, /* ← unsanitized user value */ client_dest_ip, /* ← same unsanitized value */ ipquad.local_port, ipquad.remote_port); File: lib/setopt.c, lines 1755–1763 — only length check, no format validation: case CURLOPT_HAPROXY_CLIENT_IP: result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; break; Curl_setstropt() only enforces CURL_MAX_INPUT_LENGTH — it does not validate IP address format, reject CRLF, or reject space characters. --- Proof of Concept #include <curl/curl.h> int main(void) { CURL *curl = curl_easy_init(); /* Malicious value: CRLF injection into PROXY protocol */ const char *injected_ip = "1.2.3.4\r\nPROXY TCP4 127.0.0.1 10.0.0.1 1234 80"; curl_easy_setopt(curl, CURLOPT_URL, "http://backend-server/"); curl_easy_setopt(curl, CURLOPT_HAPROXY_CLIENT_IP, injected_ip); curl_easy_perform(curl); curl_easy_cleanup(curl); return 0; } ## Impact ## Summary: An attacker who can control the value passed to CURLOPT_HAPROXY_CLIENT_IP — directly or indirectly through an application that forwards user-supplied input without sanitization — can: 1. Spoof an arbitrary source IP address in the PROXY protocol header, causing the backend server to log, trust, or act upon a fabricated client IP rather than the real one. 2. Bypass IP-based access controls on the backend by injecting a trusted internal address (e.g. 127.0.0.1 or a private subnet IP) as the reported client origin. 3. Inject a complete second PROXY protocol line via CRLF, potentially confusing HAProxy or the backend application about the true connection origin. The practical exploitability depends on whether the application passes externally controlled data (such as an X-Forwarded-For or X-Real-IP header value) directly into CURLOPT_HAPROXY_CLIENT_IP without sanitization. This is a realistic pattern in reverse-proxy and microservice architectures where the originating client IP is propagated across service boundaries.
Actions
View on HackerOne
Report Stats
  • Report ID: 3823932
  • State: Closed
  • Substate: not-applicable
Share this report