专栏首页离别歌 - 信息安全与代码审计PHP Multipart/form-data remote DOS 防御方案研究

PHP Multipart/form-data remote DOS 防御方案研究

CVE-2015-4024漏洞,据发布时间过去了好几天,我来总结一下。

这个的DOS漏洞炒得很火,百度安全攻防实验室的小伙伴也很给力。我个人认为漏洞的影响确实很大,毕竟对于一个web应用,拒绝服务攻击可以说是杀死它最简单的方法。这样大张旗鼓地说也很必要,也是一种加速杀死php 5.2/5.3的方式。

前不久才说了要赶紧弃用php 5.2/5.3事情,这次出了这么大的漏洞,很多用5.3的同学就着急了,各种求5.3的patch。

漏洞原理在drops的中文文章中(http://drops.wooyun.org/papers/6077)已经解释过了,是由于php没有妥善处理multipart/form-data请求的body part请求头,对于换行内容多次重新申请内存,导致耗尽CPU资源,拒绝服务计算机。

其实在C语言里会常常遇到这种现象,当你不知道某个buffer究竟要申请多长空间时,就必须先申请部分资源,再根据用户输入多次重新申请内存。而如果不加限制的话,就可能导致耗尽系统资源的问题。

Ryat哥很给力地带来了PHP低版本一个民间patch:https://gist.github.com/chtg/4aecda8ae4928f8fb1b2 ,方式就是限制换行次数,大于100的话就不继续分配内存了:

--- a/php-5.3.29/main/rfc1867.c
+++ b/php-5.3.29-fixed/main/rfc1867.c
@@ -464,6 +464,8 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header T
    char *line;
    mime_header_entry prev_entry, entry;
    int prev_len, cur_len;
+   int newlines = 0;
+   long upload_max_newlines = 100;

    /* didn't find boundary, abort */
    if (!find_boundary(self, self->boundary TSRMLS_CC)) {
 @@ -489,6 +491,7 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header T

            entry.value = estrdup(value);
            entry.key = estrdup(key);
+           newlines = 0;

        } else if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */

 @@ -501,6 +504,10 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header T
            entry.value[cur_len + prev_len] = '\0';

            entry.key = estrdup(prev_entry.key);
+           newlines++;
+           if (newlines > upload_max_newlines) {
+               return 0;
+           }

            zend_llist_remove_tail(header);
        } else {

当然打patch的方法,阿里云安全的同学也给出了:http://weibo.com/p/1001603844614980257697。简单说就是打补丁后,重现编译,再打包。

对于使用apt-get安装php的同学,道理也类似,一个rpm一个deb罢了。

或者,像我一样。上篇文章里我也说了,我php是用apt-get直接安装的,嫌麻烦。不过这次事情过后,我觉得万事还是需要动手,否则总是心里虚虚的。

于是我还是下载了最新源码,重新将php编译了一遍(过程大概就是这篇文章类似:http://www.jb51.net/article/42719.htm ),中间遇到的有些艰难险苦就不多说了。像我这种apt-get后再手工编译的同学,可能不知道编译选项是什么。而php的话还是很简单的,phpinfo的第三行其实就告诉我们了:

(ps. 见上图,作为一个在May 20这天还在编译php的同学,我也是很拼的)

而还是有好些同学并不会编译php,要不就是嫌麻烦,是用一键安装包安装的。那我就没法了。但如果你有装过ngx_lua_waf或modsecurity的话,可以自己编写lua脚本来临时防御漏洞。

我给出我lua waf一些代码片段吧:

postdata = split(postdata, boundary)
local i = 1
while i < #postdata do
    local lines = split(postdata[i], "\r\n\r\n")
    if #lines[0] == nil or lines[1] == nil or (not string.find(lines[0], "Content%-Disposition")) then
        ngx.exit(445)
        return true
    end
    ----defense CVE-2015-4024
    local form_data_header = split(lines[0], "\n")
    if #form_data_header > 10 then
        ngx.exit(447)
        return true
    end
    ----defense CVE-2015-4024

    local key = string.gmatch(lines[0], "%sname=\"(.+)\"")()
    local val = string.gmatch(lines[0], "filename=\"(.+)\"")()

核心思路就是用\r\n\r\n将form-data的body part分成header和body,header再用\n分割,如果数量大于10的话就直接拦截下来,返回447错误。通过这样的方式,临时抵御这次的DOS漏洞,nginx层拦截后这个数据包将不会被发送给php,所以也就不会造成DOS了。

效果如下。正常情况下上传不拦截:

当header行数超过10行,返回447错误:

但我的建议还是从根源上修补漏洞(即升级PHP到最新版本),而不是通过WAF来拦截。WAF是一个渔网,能网住大多数的鱼,但总有些小鱼是可以逃走的。况且渔网时不时地还会破几个洞,导致我们的防御形同虚设。

说些后话吧,百度安全实验室在我印象里一直很踏实,大牛很多也很低调,这次的漏洞也着实让我们看到了他们的实力。

不得不说,有时候有专业“运营”的安全团队,真还不如踏实做事的安全团队。多挖点国际漏洞增加的影响力,往往比让几个妹纸常常做些炒作、活动来的影响力要好很多。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 谈一谈php://filter的妙用

    php://filter是PHP中独有的协议,利用这个协议可以创造很多“妙用”,本文说几个有意思的点,剩下的大家自己下去体会。本来本文的思路我上半年就准备拿来做...

    phith0n
  • 利用最新Apache解析漏洞(CVE-2017-15715)绕过上传黑名单

    我在代码审计知识星球里提到了Apache最新的一个解析漏洞(CVE-2017-15715):

    phith0n
  • lnmp虚拟机安全配置研究

        众所周知,虚拟主机的安全不好做,特别是防止跨站成为了重点。apache+php服务器防止跨站的方式比较简单,网上的所有成熟虚拟主机解决方案都是基于a...

    phith0n
  • Laravel 自定义公共函数的引入

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

    泥豆芽儿 MT
  • 在laravel框架中实现封装公共方法全局调用

    在 app/Helpers/(目录可以自己随便来)下新建一个文件 functions.php,在内部补充如下代码:

    砸漏
  • SQLi-Labs环境搭建

    对于想要学习web安全的同学 , 这是一个非常好的学习有关SQL注入的学习资料 类似于闯关的模式 , 每一个关卡都有非常多的思路和利用方式 这些关卡包含了各种常...

    HACK学习
  • CTF线下AWD经验总结

    随着CTF的普及,比赛的形式也有了越来越多的花样,对于线下赛来说,常见为 AWD:Attack With Defence+公共高地、内网渗透等形式。

    HACK学习
  • The each() function is deprecated报错的解决方法

      下午ytkah安装程序时出现了如下提示,意思是each函数过时了,可能跟php版本有关,因为今天早上刚把LAMP组件升级了,php升到7.2了,切换成php...

    ytkah
  • 【代码审计】CLTPHP_v5.5.3前台XML外部实体注入漏洞

    在代码审计中,发现了微信接口存在XML外部实体注入漏洞,后面和小伙伴sn00py交流,他也发现了这个点。XML外部实体注入漏洞的代码实例比较少,这边也分享一下思...

    Bypass
  • h5ai 目录列表程序完整安装使用教程

    用户1203875

扫码关注云+社区

领取腾讯云代金券