Document

WAF绕过之sql注入bypass

前言:

接触过网络安全行业的人都知道什么是WAF,这是每一个安全人员都会面临的难题,面对市面上各种waf,绕过waf的手段也层出不穷,自己在实战的过程中也经常面临遇到waf但是缺乏绕waf的思路和方法导致渗透测试不能继续下去,在本文中我将会记录自己在学习sql注入绕waf的过程,如有不足之处欢迎各位大佬补充(#^.^#).

正文:

WAFWeb Application Firewall)的中文名称叫做“Web应用防火墙”,市面上的waf主要分为三大类,第一类是硬件类(绿盟、天融信、安恒的硬件waf)、第二类是软件类(安全狗、云锁、宝塔等软件waf)、第三类是基于云的waf(阿里云、腾讯云、创宇盾等云waf),而所谓的绕waf就是指绕过防火墙的拦截规则从而达到攻击的目的。

我之前已经知道的sql注入绕waf手段有五种,分别是大写绕过(OrDEr bY 1)、双写绕过(ororderder bbyy 1)、编码解码、内联注释绕过(/**/,/*!*/)、内存溢出绕过(就是加一些很长很长的字符串超出waf检测范围从而绕过),不过这里有些绕过手段对于很老的waf才有用,后来还了解到了http参数污染绕过和Content-Type绕过、分块传输绕过等,这些我会放在后面的一篇文章里写出来。

回归主题,先来了解一下三大数据库的注释、空白字符和语法特性

mysql oracle sql server
注释符 /**/、/*!*/、#、-- -、-- 、-- + /**/、-- +、-- 、-- - /**/、-- +、--%0A-、-- -
空白字符 %20%0A%0B%0C%0D%20 %00%20%0A%0B%0C%0D %0A%00%20

SQL语句select、union、from关键字前后可以拼接一些特殊字符,这些关键字可以让我们进行FUZZ

Mysql

[ + ]
image

image

[ ! ]
image

image

[ - ]
image

image

[ @ ]
image

image

[ ' ]
image

image

[ " ]
image

image

[ ~ ]
image

image

[ { ]
image

image

[ \N ]
image

可以看出select后面拼接+号可以解析user,花括号貌似是放正则匹配的,但是我们只要在关键字加上反引号或者括号是可以解析关键字的,from关键字后面也可以加上花括号,通过以上特性可以帮助我们FUZZ从而绕过waf,例如

image

除了mysql,Oracle和sql server也有这种特性,但是不同的是它们并不是完全支持mysql所拥有的特性,接下来我会分析Oracle和sql server拥有哪些特性。

Oracle

[ + ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ - ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ ! ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ @ ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ ~ ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ ' ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ " ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image

[ { ]
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
image
[ \N ]
image

从上面的结果和我自己另外测试的结果可以得出

  • Oracle的select关键字能添加的特殊字符有限,例如+、'、"能全面支持,-不能作用于select后面的关键字,!、@、~、{}、\N不能在select和from后面使用;
  • 反引号不会解析关键字,括号跟mysql一样可以解析关键字但是括号里不能没有东西;
  • union前加数字要加上and或or才能执行,不能加xor会报错;
Sql Server

[ + ]
image

image
[ - ]

image

image

[ ! ]
image

image

[ @ ]
image

image

[ ~ ]
image

image

[ ' ]
image

image

[ " ]
image

image

[ { ]
image

image

[ \N ]
image

从上面的结果和我自己本地测试可以得出:

  • MSSQL的union前可以添加数字,但是要加上=(0=1.1)还要有and或者or,不能加xor,否则会报错;
  • select后不能加!,@,~,{}
  • 反引号不会解析关键字,括号跟mysql一样可以解析关键字
  • MSSQL可以使用在from关键字前使用\N

MSSQL这里放个小技巧,当top限制输出不能使用的时候我们可以给前面的SQL语句加个分号结束它,然后写上set rowcount 1;就限制输出一行了,并且是全局作用范围也就是说下面所有的语句都只输出一行,要想设置回来就把1改为零即可,接着把set rowcount 1;语句删除再写上我们构造的SQL语句就可以直接限制输出了.
select a from test where id='1';set rowcount 1;-- +'

实战

直接放图吧

Mysql

image

Sql server

image

Oracle

image

我的sql注入bypass思路

我的测试流程是先看看waf过滤了什么,例如union select被过滤那么就看看是union被过滤还是select被过滤或者union select连起来被过滤,然后在被拦截的关键字前后随便填充些字符看看是否还拦截,如果不拦截就开始进行fuzz,我一般习惯在目标站点进行fuzz到不拦截再去本地数据库上测试出一条可以执行的语句然后放给目标站点执行,假如说填充了字符还是拦截的话那么久很可能函数或关键字被过滤了,这样的话我才考虑等价替换或者利用web容器特性绕过,比如说apache的HDD参数污染、IIS的%分割绕过,往后再考虑分块传输、Content-Type绕过等绕过方式。

等价替换

对于关键字或者函数过滤,除了使用内联注释分割我们还可以通过等价替换绕过,这里我就放mysql的等价替换了,对于其他数据库思路其实是一样的,先通过官方文档获取所有api函数,然后index.php?id=1 xor user()用burpsuite来爆破出没有被过滤的函数,接着去看没有被过滤的函数的用法再本地测试看看是否能组合利用,在目标站点测试的时候最好配合本地测试。

截取函数:

mid(string,1,1) <=> mid(string from 1 for 1)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
strsub(string,1,1) <=> strsub(string from 1 for 1)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
lpad(string,1,1) ldap用法:lpad(string,从string取指定数量的字符,如果取的数量超出了string内容的长度那么剩下的则用这里的替换掉)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
repacle(LPAD(user(),1,1),LPAD(user(),2,1),"") replace用法:replace(在这里进行匹配,要匹配什么,匹配到后替换成什么)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|
LPAD(reverse(trim(LPAD(user(),1,space(1)))),1,space(1)) 用法:space(生成指定数量的空格字符);trim(去除前后空格字符);reverse(把值进行反转 asp=>psa)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|

编码解码函数:

当ascii()和ord()函数被过滤时可以用conv函数替代:
conv(hex(LPAD(user(),1,1)),16,10) conv用法: conv(这里放进制值,第一位值是什么进制,要转换成什么进制)
image
|----------------------------------------------------分割线----------------------------------------------------------------------|

,逗号被过滤

,逗号在sql注入中是很重要得,多个关键字和函数都要用到,这里就说一下不用逗号的关键字和函数
if <=> case when
image
|----------------------------------------------------分割线----------------------------------------------------------------------|

union select 1,2,3 <=> union select * from (select 1)a join (select 2)b join (select 3)c
image
这里报错是因为我联合查询的字段数超过了前面那张表里的字段数所有报了个超出字段数的错误,实际上是可以用的。

|----------------------------------------------------分割线----------------------------------------------------------------------|

limit 0,1 <=> limit 1 offset 0
image

|----------------------------------------------------分割线----------------------------------------------------------------------|

比较表达式

if(abs(strcmp(ascii(substr(user(),1,1)),114))-1,1,0) 用法:strcmp()比较两个值的大小,第一个值比第二个值大就返回1,小返回-1,等于返回0;abs()求绝对值再减去一,结果为true就返回1,false返回0;
image

|----------------------------------------------------分割线----------------------------------------------------------------------|

select find_in_set(ord(substr(user(),1,1)),114) 函数用法:find_in_set(在这里进行查找,要查找的内容) 找到就返回1否则返回0
image

|----------------------------------------------------分割线----------------------------------------------------------------------|

select ord('r') regexp 114 用法:跟find_in_set原理差不多,regexp会去前面的结果查找指定的内容,找到就返回1否则返回0
image

|----------------------------------------------------分割线----------------------------------------------------------------------|

least(ord('r'),118) 函数用法:least会比较两个数值谁最小并输出出来

image

|----------------------------------------------------分割线----------------------------------------------------------------------|

greatest(ord('r'),100) 函数用法:greatest会比较两个数值谁最大并输出出来
image

|----------------------------------------------------分割线----------------------------------------------------------------------|

between x and y 用法:between设定一个区间然后去判断前面的结果是否在这个区间里,在则返回1否返回0
image

总结

写这篇文章的时候比较急,所以写的比较乱,各位大佬要是觉得有不足之处欢迎点评更正,总的来说绕waf也是一个比较深入的领域,除了这里的方法之外还有许多其他的奇淫巧技,但只要有思路甚至可以琢磨出自己的一套绕waf方式再加上市面上大部分的waf都是正则匹配可以通过像本文写的那样FUzz一下多花点时间基本都能绕过,还可以逆向软件waf的匹配库研究一下它们的匹配规则来绕过也是可行的。

文章参考资料:先知社区

posted @ 2021-09-11 20:31  掌控安全-盛夏  阅读(1357)  评论(2编辑  收藏  举报