Summary
parse_field() in darkhttpd.c uses strcasestr(conn->request, field) to locate HTTP header values. That scans the entire raw request buffer and returns the first substring match — including bytes inside the request-target or another header's value. A client can therefore smuggle arbitrary "headers" (Authorization, Range, Host, If-Modified-Since, X-Forwarded-For, ...) into request handling and logging.
The most impactful case is --trusted-ip: any client whose connection matches the trusted proxy address can spoof its logged IP by hiding X-Forwarded-For: <ip> inside a header it controls (e.g. Referer), without ever sending a real X-Forwarded-For header.
Reproduction
Start the server with a trusted proxy equal to the client address:
./darkhttpd ./wwwroot --port 18080 --addr 127.0.0.1 \
--trusted-ip 127.0.0.1 --log access.log
Send a request with no real X-Forwarded-For, but smuggle one through Referer:
printf 'GET / HTTP/1.0\r\nHost: x\r\nReferer: X-Forwarded-For: 99.99.99.99\r\n\r\n' \
| nc 127.0.0.1 18080
access.log:
99.99.99.99 - - [...] "GET / HTTP/1.1" 200 233 "X-Forwarded-For: 99.99.99.99" ""
The logged client IP is fully attacker-controlled.
Root cause
darkhttpd.c (current master):
static char *parse_field(const struct connection *conn, const char *field) {
size_t bound1, bound2;
char *pos;
/* find start */
pos = strcasestr(conn->request, field);
...
}
strcasestr does not respect header-line boundaries.
Fix
Walk header lines and only match field at the start of a line. Patch is straightforward (strncasecmp after each \n). Happy to send a PR — I have a patch + regression test ready.
Impact
--trusted-ip log spoofing (the main one — security-relevant if logs feed fail2ban / rate limiting / audit).
- Spoofing of
Authorization, Range, If-Modified-Since, Host, Connection, X-Forwarded-Proto through any header value or request-target byte the client can place.
- Hidden behind a
Referer or User-Agent header, so trivial to do through a normal browser-side request.
Summary
parse_field()indarkhttpd.cusesstrcasestr(conn->request, field)to locate HTTP header values. That scans the entire raw request buffer and returns the first substring match — including bytes inside the request-target or another header's value. A client can therefore smuggle arbitrary "headers" (Authorization,Range,Host,If-Modified-Since,X-Forwarded-For, ...) into request handling and logging.The most impactful case is
--trusted-ip: any client whose connection matches the trusted proxy address can spoof its logged IP by hidingX-Forwarded-For: <ip>inside a header it controls (e.g.Referer), without ever sending a realX-Forwarded-Forheader.Reproduction
Start the server with a trusted proxy equal to the client address:
Send a request with no real
X-Forwarded-For, but smuggle one throughReferer:access.log:The logged client IP is fully attacker-controlled.
Root cause
darkhttpd.c(current master):strcasestrdoes not respect header-line boundaries.Fix
Walk header lines and only match
fieldat the start of a line. Patch is straightforward (strncasecmpafter each\n). Happy to send a PR — I have a patch + regression test ready.Impact
--trusted-iplog spoofing (the main one — security-relevant if logs feed fail2ban / rate limiting / audit).Authorization,Range,If-Modified-Since,Host,Connection,X-Forwarded-Protothrough any header value or request-target byte the client can place.RefererorUser-Agentheader, so trivial to do through a normal browser-side request.