VYPR
Moderate severityNVD Advisory· Published Mar 30, 2021· Updated Aug 3, 2024

Possible request smuggling in HTTP/2 due missing validation of content-length

CVE-2021-21409

Description

Netty is an open-source, asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. In Netty (io.netty:netty-codec-http2) before version 4.1.61.Final there is a vulnerability that enables request smuggling. The content-length header is not correctly validated if the request only uses a single Http2HeaderFrame with the endStream set to to true. This could lead to request smuggling if the request is proxied to a remote peer and translated to HTTP/1.1. This is a followup of GHSA-wm47-8v5p-wjpj/CVE-2021-21295 which did miss to fix this one case. This was fixed as part of 4.1.61.Final.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.netty:netty-codec-http2Maven
>= 4.0.0, < 4.1.61.Final4.1.61.Final
org.jboss.netty:nettyMaven
>= 0
io.netty:nettyMaven
>= 0

Affected products

1

Patches

1
b0fa4d5aab42

Merge pull request from GHSA-f256-j965-7f32

https://github.com/netty/nettyNorman MaurerMar 30, 2021via ghsa
2 files changed · +48 2
  • codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java+4 1 modified
    @@ -353,10 +353,13 @@ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers
                     short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
                 Http2Stream stream = connection.stream(streamId);
                 boolean allowHalfClosedRemote = false;
    +            boolean isTrailers = false;
                 if (stream == null && !connection.streamMayHaveExisted(streamId)) {
                     stream = connection.remote().createStream(streamId, endOfStream);
                     // Allow the state to be HALF_CLOSE_REMOTE if we're creating it in that state.
                     allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE;
    +            } else if (stream != null) {
    +                isTrailers = stream.isHeadersReceived();
                 }
     
                 if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, "HEADERS")) {
    @@ -394,7 +397,7 @@ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers
                                 stream.state());
                 }
     
    -            if (!stream.isHeadersReceived()) {
    +            if (!isTrailers) {
                     // extract the content-length header
                     List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
                     if (contentLength != null && !contentLength.isEmpty()) {
    
  • codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java+44 1 modified
    @@ -224,10 +224,53 @@ public void headerAndDataFramesShouldBeDelivered() {
     
         @Test
         public void headerMultipleContentLengthValidationShouldPropagate() {
    +        headerMultipleContentLengthValidationShouldPropagate(false);
    +    }
    +
    +    @Test
    +    public void headerMultipleContentLengthValidationShouldPropagateWithEndStream() {
    +        headerMultipleContentLengthValidationShouldPropagate(true);
    +    }
    +
    +    private void headerMultipleContentLengthValidationShouldPropagate(boolean endStream) {
             LastInboundHandler inboundHandler = new LastInboundHandler();
             request.addLong(HttpHeaderNames.CONTENT_LENGTH, 0);
             request.addLong(HttpHeaderNames.CONTENT_LENGTH, 1);
    -        Http2StreamChannel channel = newInboundStream(3, false, inboundHandler);
    +        Http2StreamChannel channel = newInboundStream(3, endStream, inboundHandler);
    +        try {
    +            inboundHandler.checkException();
    +            fail();
    +        } catch (Exception e) {
    +            assertThat(e, CoreMatchers.<Exception>instanceOf(StreamException.class));
    +        }
    +        assertNull(inboundHandler.readInbound());
    +        assertFalse(channel.isActive());
    +    }
    +
    +    @Test
    +    public void headerPlusSignContentLengthValidationShouldPropagate() {
    +        headerSignContentLengthValidationShouldPropagateWithEndStream(false, false);
    +    }
    +
    +    @Test
    +    public void headerPlusSignContentLengthValidationShouldPropagateWithEndStream() {
    +        headerSignContentLengthValidationShouldPropagateWithEndStream(false, true);
    +    }
    +
    +    @Test
    +    public void headerMinusSignContentLengthValidationShouldPropagate() {
    +        headerSignContentLengthValidationShouldPropagateWithEndStream(true, false);
    +    }
    +
    +    @Test
    +    public void headerMinusSignContentLengthValidationShouldPropagateWithEndStream() {
    +        headerSignContentLengthValidationShouldPropagateWithEndStream(true, true);
    +    }
    +
    +    private void headerSignContentLengthValidationShouldPropagateWithEndStream(boolean minus, boolean endStream) {
    +        LastInboundHandler inboundHandler = new LastInboundHandler();
    +        request.add(HttpHeaderNames.CONTENT_LENGTH, (minus ? "-" : "+") + 1);
    +        Http2StreamChannel channel = newInboundStream(3, endStream, inboundHandler);
             try {
                 inboundHandler.checkException();
                 fail();
    

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

111

News mentions

0

No linked articles in our index yet.