High severityNVD Advisory· Published Feb 10, 2025· Updated Apr 16, 2025
SslHandler doesn't correctly validate packets which can lead to native crash when using native SSLEngine
CVE-2025-24970
Description
Netty, an asynchronous, event-driven network application framework, has a vulnerability starting in version 4.1.91.Final and prior to version 4.1.118.Final. When a special crafted packet is received via SslHandler it doesn't correctly handle validation of such a packet in all cases which can lead to a native crash. Version 4.1.118.Final contains a patch. As workaround its possible to either disable the usage of the native SSLEngine or change the code manually.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
io.netty:netty-handlerMaven | >= 4.1.91.Final, < 4.1.118.Final | 4.1.118.Final |
Affected products
1Patches
12 files changed · +19 −6
handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java+2 −0 modified@@ -1202,6 +1202,8 @@ public final SSLEngineResult unwrap( throw new NotSslRecordException("not an SSL/TLS record"); } + assert packetLength >= 0; + final int packetLengthDataOnly = packetLength - SSL_RECORD_HEADER_LENGTH; if (packetLengthDataOnly > capacity) { // Not enough space in the destination buffer so signal the caller that the buffer needs to be
handler/src/main/java/io/netty/handler/ssl/SslUtils.java+17 −6 modified@@ -314,8 +314,12 @@ static SSLHandshakeException toSSLHandshakeException(Throwable e) { * the given {@link ByteBuf} is not encrypted at all. */ static int getEncryptedPacketLength(ByteBuf buffer, int offset, boolean probeSSLv2) { + assert offset >= buffer.readerIndex(); + int remaining = buffer.writerIndex() - offset; + if (remaining < SSL_RECORD_HEADER_LENGTH) { + return NOT_ENOUGH_DATA; + } int packetLength = 0; - // SSLv3 or TLS - Check ContentType boolean tls; switch (buffer.getUnsignedByte(offset)) { @@ -346,7 +350,7 @@ static int getEncryptedPacketLength(ByteBuf buffer, int offset, boolean probeSSL tls = false; } } else if (version == DTLS_1_0 || version == DTLS_1_2 || version == DTLS_1_3) { - if (buffer.readableBytes() < offset + DTLS_RECORD_HEADER_LENGTH) { + if (remaining < DTLS_RECORD_HEADER_LENGTH) { return NOT_ENOUGH_DATA; } // length is the last 2 bytes in the 13 byte header. @@ -367,7 +371,8 @@ static int getEncryptedPacketLength(ByteBuf buffer, int offset, boolean probeSSL packetLength = headerLength == 2 ? (shortBE(buffer, offset) & 0x7FFF) + 2 : (shortBE(buffer, offset) & 0x3FFF) + 3; if (packetLength <= headerLength) { - return NOT_ENOUGH_DATA; + // If there's no data then consider this package as not encrypted. + return NOT_ENCRYPTED; } } else { return NOT_ENCRYPTED; @@ -420,24 +425,29 @@ static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) { } // We need to copy 5 bytes into a temporary buffer so we can parse out the packet length easily. - ByteBuffer tmp = ByteBuffer.allocate(5); + ByteBuffer tmp = ByteBuffer.allocate(SSL_RECORD_HEADER_LENGTH); do { buffer = buffers[offset++].duplicate(); if (buffer.remaining() > tmp.remaining()) { buffer.limit(buffer.position() + tmp.remaining()); } tmp.put(buffer); - } while (tmp.hasRemaining()); + } while (tmp.hasRemaining() && offset < buffers.length); // Done, flip the buffer so we can read from it. tmp.flip(); return getEncryptedPacketLength(tmp); } private static int getEncryptedPacketLength(ByteBuffer buffer) { + int remaining = buffer.remaining(); + if (remaining < SSL_RECORD_HEADER_LENGTH) { + return NOT_ENOUGH_DATA; + } int packetLength = 0; int pos = buffer.position(); + // SSLv3 or TLS - Check ContentType boolean tls; switch (unsignedByte(buffer.get(pos))) { @@ -478,7 +488,8 @@ private static int getEncryptedPacketLength(ByteBuffer buffer) { packetLength = headerLength == 2 ? (shortBE(buffer, pos) & 0x7FFF) + 2 : (shortBE(buffer, pos) & 0x3FFF) + 3; if (packetLength <= headerLength) { - return NOT_ENOUGH_DATA; + // If there's no data then consider this package as not encrypted. + return NOT_ENCRYPTED; } } else { return NOT_ENCRYPTED;
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-4g8c-wm8x-jfhwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-24970ghsaADVISORY
- github.com/netty/netty/commit/87f40725155b2f89adfde68c7732f97c153676c4ghsax_refsource_MISCWEB
- github.com/netty/netty/security/advisories/GHSA-4g8c-wm8x-jfhwghsax_refsource_CONFIRMWEB
- security.netapp.com/advisory/ntap-20250221-0005ghsaWEB
- www.vicarius.io/vsociety/posts/cve-2025-24970-netty-vulnerability-detectionghsaWEB
- www.vicarius.io/vsociety/posts/cve-2025-24970-netty-vulnerability-mitigationghsaWEB
News mentions
0No linked articles in our index yet.