Case study · OpenBSD relayd · CWE-444

Thirteen years of HTTP request smuggling in OpenBSD’s relayd

A single-header conformance defect in relay_http.c left OpenBSD’s HTTP reverse proxy vulnerable to classic CL.TE request smuggling from the 5.2 release in 2012 onwards. Found by targeted source review against the RFC 9112 framing rules; fixed in −current on 2026-06-03 in a single commit.

4 June 2026 · OpenBSD relayd · CWE-444 (HTTP Request Smuggling) · remote, pre-auth
Full technical disclosure: stuart-thomas.com/research/relayd-cl-te-smuggling/

What the defect was

OpenBSD’s relayd is the project’s daemon for reverse-proxying and load-balancing TCP, HTTP and HTTPS traffic to backend services. When forwarding an HTTP message whose framing uses Transfer-Encoding: chunked, relayd parsed the body correctly as chunked — but it did not remove a co-present Content-Length header before passing the message to the backend.

That is contrary to the explicit rule in RFC 9112 §6.1, which the practice reproduces below for the reader:

“A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field.”

— RFC 9112 (HTTP/1.1), §6.1, June 2022

The conformant behaviour for any HTTP proxy in this situation is either to reject the request or to strip the Content-Length header before forwarding. relayd did neither: it parsed using the chunked framing it had chosen, and forwarded both headers to the backend.

Where the backend prefers Content-Length when both headers are present — which many HTTP backends do — the proxy and the backend disagree on where one request ends and the next begins on the same TCP connection. The disagreement is the smuggling primitive. The textbook label for this variant is CL.TE: front-end uses Content-Length (here, the back-end), back-end uses Transfer-Encoding (here, relayd — or vice versa). The effect is identical.

The practical operational impact, on any deployment where relayd sits in front of an HTTP backend with a divergent framing preference, is the standard CWE-444 set:

The practice has deliberately not published exploit payloads. The point of the disclosure is to allow operators to patch, not to ship a working smuggling crowbar against unpatched deployments. The bytes-on-the-wire required to exploit any given (relayd + backend) pairing depend on the backend’s exact framing preference and are not enumerated here.

Why this survived thirteen years

The current HTTP forwarding code in relay_http.c dates from OpenBSD 5.2 (released November 2012). The defect has been latent from that release through OpenBSD 7.9 inclusive — thirteen years of release branches.

HTTP request smuggling as a class was first systematised in 2005 (Linhart, Klein, Heled and Orrin, published by Watchfire) and then popularised across the security industry by PortSwigger’s 2019 research, after which most large-scale front-end proxies (Apache, nginx, HAProxy, IIS, CDN edges) were audited and updated against the specific RFC-conformance rules that govern conflicting framing.

relayd did not get that scrutiny. The reasons are reasonable in hindsight:

Which is what happened in this case.

The methodology lesson. The find came from reading relay_http.c with RFC 9112 §6.1 open in the other window and asking a single question of each header-processing branch: does this code do what the RFC requires when both Content-Length and Transfer-Encoding are present? That is exactly the kind of bounded, RFC-anchored source review a small lab can deploy without large infrastructure, and it is the kind of bug fuzzing and generic static analysis are statistically unlikely to find — the trigger is “send a well-formed HTTP message with both framing headers,” which a fuzzer can produce but which neither the proxy nor the backend treats as malformed in isolation.

The fix

The fix landed in OpenBSD −current on 2026-06-03 in commit e8e5aa2db9cf7bcd254dadc5f14a69547006e9a2. The change lives in usr.sbin/relayd/relay_http.c and is small — when relayd determines that an inbound HTTP message uses chunked framing, the Content-Length header is stripped from the outbound message before forwarding to the backend, per RFC 9112 §6.1.

Fix availability

The commit is in OpenBSD −current as of 2026-06-03. Release timing and any backport to the supported −stable branches are the OpenBSD project’s call; the practice does not speculate on them.

The authoritative sources are:

Interim posture for operators running relayd in HTTP forwarding mode (i.e., not pure TLS termination or layer-4 TCP relay) on OpenBSD 7.9 or earlier: either run a −current snapshot of relayd against the patched relay_http.c, or front relayd with a proxy known to reject conflicting Content-Length and Transfer-Encoding headers per RFC 9112 (current-vintage nginx or HAProxy, for example), so that no smuggled framing reaches relayd in the first place. Deployments using relayd purely for TLS termination or layer-4 TCP relay are unaffected; the HTTP code path is not entered.

Timeline

DateEvent
2012-11OpenBSD 5.2 released; current relay_http.c HTTP forwarding code present in this form. Defect latent from here onward.
2026-05-28Reported to bugs@openbsd.org.
2026-06-03Fixed in OpenBSD −current in commit e8e5aa2db9c.
2026-06-04This case study published.

What this case study is not

Full technical disclosure

stuart-thomas.com/research/relayd-cl-te-smuggling/ — full per-section detail, references, legal note.

Commit references

Fix: github.com/openbsd/src/commit/e8e5aa2db9c

File: cvsweb.openbsd.org/log/src/usr.sbin/relayd/relay_http.c,v?sort=File

Standard

RFC 9112 (HTTP/1.1), §6.1 — rfc-editor.org/rfc/rfc9112.html§6.1

Credit

Discoverer: Stuart Thomas (TriageForge). The OpenBSD project is named in its public capacity as maintainer of the affected software; individual developers involved in the fix are credited in connection with their own public commits.

Legal note

This case study is published under the Defamation Act 2013 facts-and-opinion convention. Commit hashes, dates, file paths, and the cited RFC text are accurate to the best of the author’s knowledge and are evidenced by the public github.com/openbsd/src mirror and RFC 9112 as published by the IETF. Detailed exploit payloads are intentionally not published. Research was conducted on hardware owned by the author; no third-party systems were accessed in the course of the research. The work was performed within the scope permitted by the Computer Misuse Act 1990 (England and Wales) own-hardware exemption.