
引言
SQL注入在我们日常的渗透中是属于常见的一类漏洞,但是最令我们头疼的不是什么参数化查询,而是WAF。但是如果直接介绍的话,我发现效果并不好。因为我本身作为一个学习者,发现很多的文章只是单单的把姿势给写出来,但却不介绍以及解释这些姿势背后的特性,以及可能会踩雷的地方,这对初学者极其不友好。正好,我最近自己也在学习绕WAF的方式,就把自己的一些学习所得,进行一个记录以及分享,主要是给大家参考我学习的一个思路,介绍的姿势其实不一定能够绕过WAF,因为不同的WAF绕过的语句都是不同的,还是要通过SQL的官方文档上面的语句自己进行一个多种结合的尝试才可能实现绕过WAF~
目录
本文涉及的函数有:if | case when ... then ... else ... end | IFNULL | NULLIF
注意事项
1.本文的场景默认以:SELECT * FROM users WHERE id='$id' LIMIT 0,1 其中$id是我们的输入点,也就是sqllabs靶场第一关为例。也会涉及到SELECT * FROM users WHERE id='$id' 场景~
2.本次讨论只是 if 被过滤的情况,其他情况不作为本次考虑范围内。
3.文章只讨论布尔注入以及时间注入两种
4.非小白师傅可以直接跳过第一个知识点
正式开始
知识点一:if基础使用
说到if的时候,最为经典也是常用的两种注入类型:布尔注入、时间注入
在这两种注入中,if是我们绕不开的关键字。
例如:
数据库名称为security的前提下
1' and if(database()='security',1,0) --+
1' and if(database()='sec',1,0) --+
逐一介绍:
if在mysql中的语法是:if(a=b, c, d) --> 当a=b的时候,返回c,否则返回b
if(database()='sec',1,0) --> 当数据库名为sec的时候,if(database()='sec',1,0)会等于1,否则会等于0
那么我们说了,数据库名称为security的前提下,所以if(database()='sec',1,0)会等于0。从而不返回数据。
这时候有人问了,主播主播,为什么返回0了就不回显数据了?
因为这个和 and 有关系,完整的语句是:
这里将 $id 替换为1' and if(database()='sec',1,0) --+
SELECT * FROM users WHERE id='1' and if(database()='security',1,0) --+ LIMIT 0,1
and的逻辑是:
A and B ,只有当A和B同时满足(同时为true)才会返回结果。
1=1 and 1=2 --> 1=1满足了(true),1=2不满足(false),这样就不会返回结果,必须是:
1=1 and 2=2 --> 这样两个都同时相等,同时为(true),才会返回结果
而1和0,你可以理解为代表着true和false,但值得注意的是1和0并不是等价于true与false。
他们其中的逻辑是这样的id='1' and if(database()='security',1,0),如果当前数据库名是security,if()函数返回1(真),此时整个WHERE子句变为id='1' and 1,就会去查询id=1的数据。反之,id='1' and if(database()='sec',1,0),此时整个WHERE子句变为id='1' and 0,在and表达式中,0 --> false,id='1' and false,有一个假条件,所以这个and句子就失效了,不会去执行查询id='1'的过程。
简单来说:1和0分别代表着true和false,当我们and中有false的时候,就不会返回(查询出)任何的数据。
好,现在将所有的概念连在一起:
第一句:SELECT * FROM users WHERE id='1' and if(database()='security',1,0) --+ LIMIT 0,1
第二句:SELECT * FROM users WHERE id='1' and if(database()='sec',1,0) --+ LIMIT 0,1
第一句逻辑if(database()='security',1,0)因为数据库名字为security,所以返回了1 --> true,SELECT * FROM users WHERE id='1'成功进行了查询,所以有数据回显。
第一句逻辑if(database()='sec',1,0)因为数据库名字为security,所以返回了0 --> false,SELECT * FROM users WHERE id='1'没有进行查询,所以没有数据。
理解了上面的,就很好理解下面的常用测试语句:
1' and if('s'=substr(database(),1,1),1,0) -- -
1' and if('x'=substr(database(),1,1),1,0) -- -
substr(database(), a, b)的作用很简单,substr用于切割字符串,a表示切割第几位,b表示切割的长度。
例如:
substr('123456789', 1, 1) --> 1, substr('123456789', 2, 1) --> 2
substr('123456789', 1, 5) --> 12345, substr('123456789', 2, 5) --> 23456
这里我们就是把数据库名字:security 进行了第一位的切割,切割出的结果是:s
if('s'=substr(database(),1,1),1,0) 因为substr(database(),1,1)确实等于s,所以if就返回了1,有数据
if('x'=substr(database(),1,1),1,0) 因为substr(database(),1,1)不等于x,所以if就返回了0,没有数据
那么布尔注入
其实就是把if('X'=substr(database(),1,1),1,0)中的X设置为爆破的目标,使用手工或者工具,将所有的大小写26位字母以及数字进行一次尝试,最终通过if('s'=substr(database(),1,1),1,0),if('s'=substr(database(),2,1),1,0),if('s'=substr(database(),3,1),1,0)一位一位的将数据库名称给试出来
时间注入就更简单了
1' and if('s'=substr(database(),1,1),1,sleep(5)) -- - 这段语句,只要x中的字符不等于数据库的名称,就会响应5秒。如果等于,就会直接返回结果
1' and if('s'=substr(database(),1,1),1,sleep(5)) -- -
补充:
1' and if(114=ascii(substr(user(),1,1)),1,sleep(4))-- - 这个其实和前面没有区别,ascii码是为了更加的便捷进行以及不容易出错进行注入。
Mysql中ascii码转的是十进制,也就是这里114 --> 在ascii码表中对应的是 r 。
ascii函数也很简单,就是把字符转为ascii码,例如字符串为r --> 114 码
但有个特性:仅处理第一个字符:无论字符串多长,只取第一个字符计算 ASCII 码。
所以才需要使用切片函数substr()
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)