Discuz! X3.2 隐藏回复BUG导致的CC攻击分析
2015-02-28 tech

TL;DR,这篇文章主要是分析我研究这个漏洞产生的过程的,如果你只想知道怎么修改,请直接跳过看结尾。
昨天写了70%,然后忘记保存出悲剧了,写的全部报销,于是咱就重新写吧。
昨天晚上闲着没事干的时候,白翼抄送了一份Discuz!X3.2的漏洞给我,然后我就开始试着分析这个漏洞。
漏洞的触发方法如下(由GDF总结,在此引用并感谢)

1.发表主题,内容无所谓
2.回复此主题,上传几个图片附件,并插入帖子内,用[hide]隐藏图片
3.重复第2步几次~~~也就是多回复几个这样的帖子
4.换个不能直接查看隐藏附件权限的号回复

然后,你的浏览器就会在无意之中发起对服务器发起CC攻击,发起数千个请求,与此同时你的浏览器当然也跟着跪了。
当然,要使用这个漏洞,要满足以下条件:

  • 首先你的帐号得有发隐藏贴的权限
  • 然后论坛得是X3.2版本的

不得不说要这个条件还是蛮苛刻的,所以大家也不用过分担心了,没有注入漏洞来的严重。
好了,然后用别的工具大概抓了下包
「PS:这时候浏览器都崩溃了,就别想着用浏览器自身的Debug工具来抓了吧,换了个工具上」
发现其不断请求forum.php?mod=viewthread&tid={a}&viewpid={b}&inajax=1&……

好,大概有思路了,然后查看下请求返回的内容,大致如下

1
2
3
<root>
<![CDATA[<script type="text/javascript" reload="1">if(typeof succeedhandle_fastpost=='function') {succeedhandle_fastpost('forum.php?mod=viewthread&amp;tid={a}&amp;pid={b}&amp;page=1&amp;extra=#pid{b}', '非常感谢,回复发布成功,现在将转入主题页,请稍候……[ 点击这里转入主题列表 ]', {'fid':'{c}','tid':'{a}','pid':'{b}','from':'','sechash':''});}</script>]]>
</root>

嗯,我们能够判断,大概是succeedhandle_fastpost这个JS函数引起的
然后就和小新一起来研究这个BUG了…

好,先让咱大概看看文件位置吧,在static/js/forum_viewthread.js里的228行开始的地方

然后你总不能空口无凭判断这个函数有问题吧,那么我就在这个函数的头两行直接加

1
2
location.href= locationhref;
return '';

来测试是否能够正常回复,OK,谢天谢地,正常返回,不会CC。
可以肯定是这块的问题了。
然后缩小路径,开始限定在这个函数的如下位置

刚开始绕了个弯,我和小新以为可能是reloadpids这个变量的问题,然后导致下面length不正常,然后检测了这个变量会被抛回什么内容,结果就是一串用半角逗号隔开的需要用ajax重载的pid列表。

好,这块正常,然后我们只能怀疑是这个循环本身出问题了。
于是打算输出一个数字看看

加了行console.log输出数字,然后返回结果。。。嗯。。如下图

到这里想必都明白了,i这个循环计数器在中途给改了!!!唉。。。
于是咱就改改标号吧,换成loop_i试试

嗯,终于无痛解决了,谢天谢地。
后来也分析了一下,在另外一个js文件里面,调用ajaxget的这个过程貌似又出了一个i把这个变量给盖掉了,所以这里才掉进循环坑里,唉。
「PS:我可不会乱猜测这出BUG的猴子和我一样都是懒癌晚期23333333」
和小新花了半个来小时把这个漏洞给过了一遍,基本上到这里算是结束了吧。

说到解决方法,下面提供三种:
「建议使用第三种,当然你觉得这个漏洞无关紧要,可以不改,等官方补丁也行,就是什么时候出来,不保证。」
1.限定一定时间内的单用户的最大请求个数(这个呢,在DZ的config里也可以,但是用PHP干这件事情不太好,如果是用nginx跑站的,在nginx加参数也是可以的)
2.限制隐藏贴标签的使用权限,保证只有友军能够用。
3.修改static/js/forum_viewthread.js的236-238行把

1
2
3
for(i = 1;i < reloadpids.length;i++) {
ajaxget('forum.php?mod=viewthread&tid=' + tid + '&viewpid=' + reloadpids[i] + '&from=' + from, 'post_' + reloadpids[i], 'ajaxwaitid');
}

改为

1
2
3
for(var i = 1;i < reloadpids.length;i++) {
ajaxget('forum.php?mod=viewthread&tid=' + tid + '&viewpid=' + reloadpids[i] + '&from=' + from, 'post_' + reloadpids[i], 'ajaxwaitid');
}

保存即可。
「PS:修改i为局部变量,感谢KK后来提醒:)」

终于写完了,不容易,休息去。。。