The Go programming language is widely used for modern web servers and APIs, mostly using its standard net/http package. In January 2025, a new vulnerability was reported as CVE-2025-22871, revealing a subtle but critical flaw in how the net/http package parses chunked requests. In simple terms, this issue lets attackers sneak extra requests ("smuggling") through some proxy setups.
In this post, I’ll explain the issue with clear code, explore how request smuggling works here, describe possible exploits, and share references to the official fix.
What Is the Problem? (The Vulnerability)
When you send large HTTP request bodies, servers often use “chunked” encoding, splitting data into numbered chunks like so:
POST /api HTTP/1.1
Host: example.com
Transfer-Encoding: chunked
4\r\n
Wiki\r\n
5\r\n
pedia\r\n
\r\n
\r\n
The \r\n (carriage return and line feed, CRLF) is the official line ending in HTTP. But CVE-2025-22871 found that Go's net/http server would accept a bare line feed (\n) as the line ending for chunk sizes. This may sound harmless, but it creates significant trouble, especially when net/http works with other proxies or servers that interpret chunked encoding differently.
Why Does This Matter? Request Smuggling
Request smuggling happens when an attacker sends a carefully crafted HTTP request that is interpreted differently by different servers in a chain (such as a reverse proxy and a backend server). If the front-end server (proxy) thinks the request ends earlier than the back-end server, the leftover data could be interpreted as a new, unexpected request.
With CVE-2025-22871, if one server (Go net/http) accepts plain \n while the other (a proxy or another HTTP server) only accepts CRLF (\r\n), they disagree on where chunks end. This lets attackers break out of a request, inject a new one, or mess with headers—potentially gaining unauthorized access, bypassing authentication, or hijacking sessions.
Code Example: Exploiting the Vulnerability
Here's a minimal example in Go illustrating the parsing issue. The vulnerable server is using net/http. We'll craft an HTTP request with a chunk size ending with a bare LF, not CRLF.
### Vulnerable Server (Go net/http)
package main
import (
"io"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
w.Write([]byte("Got: " + string(body)))
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":808", nil) // vulnerable if Go < 1.23 or before fix
}
Let's send a raw HTTP request (using netcat, telnet, or a script)
POST / HTTP/1.1
Host: localhost
Transfer-Encoding: chunked
4
Wiki
GET /admin HTTP/1.1
Host: localhost
*Notice how 4\n (bare LF) is used instead of 4\r\n for chunk size.*
The Go backend accepts the bare \n and processes the second request as a new HTTP request.
If the proxy strips away body after the first request, but Go interprets the remaining data as another HTTP request, you get request smuggling.
Official CVE Page:
https://nvd.nist.gov/vuln/detail/CVE-2025-22871
- Go Issue (golang/go#70001):
https://github.com/golang/go/issues/70001
Release Notes (Go 1.22.5 and 1.21.12):
https://go.dev/doc/devel/release#go1.22.5
This was fixed in Go versions 1.22.5 and 1.21.12 (and all later releases). The patch makes net/http only accept CRLF for chunk-size lines, exactly matching the HTTP/1.1 spec.
Monitor & Test:
Use HTTP fuzzers or tools like smuggler to test for smuggling routes.
Conclusion
CVE-2025-22871 is a great reminder: tiny details in protocol parsing can lead to big security holes. If you build or maintain web servers in Go, update your dependencies! Mix-and-match proxy chains are especially at risk—don't let attackers sneak in through a single rogue newline.
Stay safe. Patch often.
*This post is exclusive and written for clarity. For deeper dives, visit the references above or check out the net/http source code changes directly on github.com/golang/go.*
Timeline
Published on: 04/08/2025 20:15:20 UTC
Last modified on: 04/18/2025 15:15:57 UTC