[投稿]从CVE-2014-0166看高效率EXP的编写

## 前言

每当各CMS 0day初现的季节,就是黑客们疯狂写exp的季节。虽说写exp是黑客的基本功,但要写出一个漂亮的exp并不是那么容易的。这篇文章我就以**CVE-2014-0166**的exp编写为例,谈一谈如何编写高效exp。

## 关于CVE-2014-0166

CVE-2014-0166是WordPress的一个Cookie伪造漏洞。如果之前不了解这个漏洞,请先看详细分析

简言之,就是在比较cookie中的hash和有效hash时,使用了_non-strict comparison operator_(”!==”),从而导致形如0e243724638263727679836827364732 == 0的一类hash错误匹配问题,造成伪造cookie的可能。

这篇文章中我们会详解针对该漏洞的**POC**(本地验证)和**EXP**(在线穷举)的编写。

## 本地验证POC的编写

POC即_Proof of Concept_,翻译过来就是“概念证明”。POC的作用只是用来证明漏洞的存在,这个证明可以是概念上的,而不一定是直接利用漏洞的程序。

通过对CVE-2014-0166的分析可知,平均遍历300000000个_expiration time_(变量名$expiration)才可以找到一个形如0e243724638263727679836827364732的hash(为方便,后文将该类型hash称为_zero hash_)。

我们知道,真正利用该漏洞时,每遍历一个$expiration都要发送一次请求,三亿个请求再快也要十天半个月才能遍历完。而我们在发送POC给别人验证时不可能让人家开这机子跑那么长时间,因此就需要进行本地验证,免去发送数据的过程。我们的目的,仅仅是找到一个expiration使得$hash == 0

先看最直接的POC,只是摘取了WordPress中对应的验证部分代码,添加上遍历$expiration的功能,一个POC就诞生了(点此看POC的完整代码)。看关键部分代码:

每次遍历一个$expiration,并求出相应的hash,然后与0比较,相等则输出hash并结束循环。

但程序写出来一跑,才发现要遍历3亿次还是需要好几个小时。我laptop是八核的,观察cpu使用情况却发现只占用了一核。于是考虑用python改写为**多进程**程序(点此看完整代码),将8个核全部利用起来:

以8个进程为例,把expiration的累加拆分成8个数同时以步长为8进行累加。这样速度就快了8倍。

以遍历8到15为例:
单进程:7+1=8 8+1=9 9+1=10 10+1=11 11+1=12 12+1=13 13+1=14 14+1=15
多进程:0+8
1+8
2+8
3+8
4+8
5+8
6+8
7+8
可见单进程需要8次循环的时间,而多进程8次循环同时进行,只需要一次循环的时间。

我们设定好pass_frag,auth_key,auth_salt等信息,然后就可以运行脚本开始跑了:
LocalPOC

## 远程EXP的编写

由于利用该漏洞需要大量的请求包,在编写远程EXP时,效率方面的考虑就显得更为重要。

我们考虑从以下两个方面来提高EXP的效率:

* 减少单次请求的数据量
* 缩短由于网络延迟而等待的时间

第一方面,减少数据通行量。我们选择通过**HEAD**请求,访问/wp-admin/,然后判断response包的**status code**,若为**302**则不成功,**200**则成功。这样就可以省去response包的body部分数据,数据量大大减少。

第二方面,缩短网络延迟等待时间。我们使用**多线程**(_multithreading_),从而可以并发运行,在网络延迟的空闲时间继续发送与接收数据包。

除了这两个方面,我们还可以选择进行一些更进一步的优化。比如**自动调整线程数**从而减少由于线程数过高而导致的服务器错误,又比如加入简单的**分布式支持**,从而能将脚本部署到多台机器上分工运行。

点此阅读代码,然后我在下面摘取一些关键部分进行说明:

## HEAD请求减少数据通量

这个函数是用以测试cookie是否有效的。正如前文说的,对/wp-admin/发送head请求,判断返回值。若返回200,返回cookie值;若返回302,返回False;返回其他(如500),返回’Err’用以记录错误次数。

## 多线程的调度

这是多线程部分,注意到为了实时输出进度,在输出尝试次数时调用了**线程锁**(lock.acquire()),防止输出混乱。之后再释放线程锁,发送请求包时。这样就既保证了输出正常,也 保证多线程充分发挥作用。

## 智能降低线程数

这里首先引入一个变量errTolerance,用以指定错误容忍度,值为所容忍的错误百分比,比如容忍10%的错误(100个请求中有10个返回错误),则errTolerance的值为0.1,类似的,值为0时为零容忍。变量errRate为错误率,由之前testCookie函数返回的’Err’的个数除以总请求数求得。之后判断如果errRate大于errTolerance,则将线程数降低0.5*errRate倍。

最后的效果是这样的:
Adjust threads

## 简单的“分布式”

之所以我在_分布式_上打引号,是因为这是最简陋的分布式,或者说就称不上分布式。只是在初始值上简单的加上一个**断点值**initnum。这样可以把脚本放到多个vps上,每个设定不同的initnum,即可达成简陋的“分布式”效果。另外,也可以用于“断点续算”。考虑到一天肯定跑不出结果,所以可以记下当天进度,次日设置initnum以继续。

## 总结

就CVE-2014-0166这个漏洞来说,利用成本与密码爆破相当,价值其实不大。我在一个VPS上测试,跑了将近五天,才跑出一个:
Result

但这个漏洞的思想十分值得玩味,另外,不得不说,我也十分享受为这个漏洞写POC和EXP的过程。这也是我迫切想和大家分享下其中一些技巧的原因。我相信,这些技巧在今后的EXP或是其他工具的编写过程中一定也会用到。

PS:本文涉及到的所有代码都可以在<https://github.com/Ettack/POC-CVE-2014-0166/>找到

 

[vulAnalyze]: http://www.ettack.org/wordpress-cookie-forgery/
[POC1]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/wp_zero_cookie_generator.php
[POC2]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/zeroCather.py
[EXP]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/cookieForger.py
[zeroCatcher]: https://static-js.b0.upaiyun.com/wp-content/uploads/auto_save_image/2014/05/03492975w.jpg
[threadsAdjusting]: https://static-js.b0.upaiyun.com/wp-content/uploads/auto_save_image/2014/05/034932584.jpg
[vpsTrying]: https://static-js.b0.upaiyun.com/wp-content/uploads/auto_save_image/2014/05/034936nWS.jpg

赏金发放情况:本文获得赏金100RMB,已于5.9日发放到作者账号。

征稿启事:91RI一直相信“你不与人分享,谁与你分享”, 分享的确是件非常有意义的事情。为了让优秀的同学有 地方分享自己的独到见解,也为了让更多同学从分享中受益,同时我们也希望给那些愿意分享的小伙伴们一点点心意作为感谢,所以我们隆重了推出“有奖征文”活 动!本次活动的详情可以围观《征稿启事