PHP远程DoS漏洞深入分析及防护方案

5月14日,国内爆出php远程DoS漏洞,官方编号69364。利用该漏洞构造poc发起链接,很容易导致目标主机cpu的占用率100%,涉及PHP多个版本。绿盟科技威胁响应中心随即启动应急机制, 应急响应工作随即启动。

  1. 15日夜,启动漏洞分析工作,同步将分析结果发送产品团队;

  2. 16日,发布产品规则升级通告,绿盟科技RSAS产品升级相继就绪,客户通过在线及离线升级的方法,即可获得漏洞的检测能力;同时,在线漏洞检测引擎就绪;

  3. 17日,漏洞深入分析进行中。绿盟科技NIPS产品升级就绪,客户通过在线及离线升级的方法,即可获得漏洞的防护能力;

  4. 18日,我们回顾此次PHP漏洞的信息要点,从PHP漏洞防护的角度进行总结,为大家制定防御方案提供补充信息。


PHP远程DoS漏洞

4月3日,有人在PHP官网提交PHP 远程DoS漏洞(PHP Multipart/form-data remote dos Vulnerability),代号69364。由于该漏洞涉及PHP的所有版本,故其影响面较大,一经发布迅速引发多方面关注。14日,各种PoC已经在网络上流传。此次漏洞具备如下特性:

  1. 一旦被利用成功,可以在迅速消耗被***主机的CPU资源,从而达到DoS的目的;

  2. PHP在全球的部署量相当大,为***者提供了相当多可以***的目标;

  3. PHP官方目前仅给出了5.4及5.5版本的补丁受此漏洞影响的软件及系统包括PHP的如下版本。
    PHP 5.0.0 – 5.0.5
    PHP 5.1.0 – 5.1.6
    PHP 5.2.0 – 5.2.17
    PHP 5.3.0 – 5.3.29
    PHP 5.4.0 – 5.4.40
    PHP 5.5.0 – 5.5.24
    PHP 5.6.0 – 5.6.8

绿盟科技常年密切关注PHP的安全问题。绿盟科技威胁响应中心在获知相关信息后,随即启动应急机制,相关工作随即启动。本文章将会深入分析该漏洞,并给出应对方案。

PHP远程DoS漏洞分析

2015年5月15日夜,绿盟科技威胁响应中心在获取PHP漏洞传播情况的同时,也在进行漏洞的分析工作,通过重现漏洞的***过程,分析其工作原理,得以清晰识别及检测该漏洞方法。

Boundary中的键值对分隔

PHP是一种流行的Web服务器端编程语言,它功能强大,简单易用,利用它编写网络应用程序,可以应对大规模的Http请求,所以很多业务环境中都部署了PHP。考虑规范性,PHP在设计之初就遵循rfc规范,进行各个协议模块的封装及过程处理。PHP与其他同样遵循rfc规范的语言及环境相比,不过是处理方式不同。

而从rfc1867开始,http协议开始支持”multipart/form-data”请求,以便接受多种数据格式,包括多种变量甚至是文件上传。multipart/form-data中可以包含多个报文,每一个报文boundary(分隔符)分隔开来,而每个报文中都包含了多行键值对,键值对用冒号分隔,这样的设计是为了让程序可以清晰的区分这些数据。

PHP远程DoS漏洞深入分析及防护方案

但如果由于某种原因,键值中间缺少了那个冒号,PHP函数会将下一对键值合并到了上一行,形成这样的键值对,“键1:值1键2值2”。由于PHP进行键值合并的算法不够优化,这样的事情发生几次还没什么,如果数以百万记,就变成了一种灾难。

在下面的例子中,当a的部分达到一定数量的时候(几十万行or上百万行),由于每行键与值之间并没有冒号分隔,函数就自动将下一行的键值对合并,这样数据越来越大,越来越长,函数针对这些数据不断执行内存的分配和释放,最终被***目标主机的CPU资源被耗尽。

PHP远程DoS漏洞深入分析及防护方案

*注:PHP中,Boundary是可以自定义的,比如“—–WebKitFormBoundarypE33TmSNWwsMphqz”

这样的代码,在抓包时显示情况如下

PHP远程DoS漏洞深入分析及防护方案

Boundary报文解析过程

PHP在main/rfc1867.c中,有两个函数都涉及边界的解析,包括SAPI_API SAPI_POST_HANDLER_FUNC及multipart_buffer_headers函数,漏洞出现在主要/rfc46675pxultipart_buffer_headers函数。

PHP先解析解析多部分/格式http请求,http请求体的入口函数在SAPI_POST_HANDLER_FUNC (rfc1867。c中的函数),SAPI_POST_HANDLER_FUNC函数首先解析请求的边界,也就是帖子请求中第一次定义时的边界,并且在其内部调用了multipart_buffer_headers,该函数先找到边界(也就是一次引用的边界),会和定义时的边界比较。如果相等即找到第一次引用的边界,接下来会逐行读取请求的输入以解析身体港口头(也就是解析第一次引后用边界面的内容)。

PHP远程DoS漏洞深入分析及防护方案