这个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注入字符串,再访问漏洞页面即可。
脚本:
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值。不过,还是可以爆破出来的,理论上…………