PHP multipart/form-data 远程DOS漏洞

PHP解析multipart/form-datahttp请求的body part请求头时,重复拷贝字符串导致DOS。远程攻击者通过发送恶意构造的multipart/form-data请求,导致服务器CPU资源被耗尽,从而远程DOS服务器。
影响范围:

PHP所有版本

0x01 漏洞入口

PHP源码中main/ rfc1867.c负责解析multipart/form-data协议,DOS漏洞出现在main/rfc46675pxultipart_buffer_headers函数。
在详细分析漏洞函数前,先分析进入漏洞函数的路径。PHP解析multipart/form-data http请求体的入口函数在SAPI_POST_HANDLER_FUNC(rfc1867.c中的函数),代码如下。

SAPI_POST_HANDLER_FUNC函数首先解析请求的boundary,

0x02 漏洞函数multipart_buffer_headers执行逻辑

进入漏洞函数,本段先分析漏洞函数的执行逻辑,下一段根据函数执行逻辑详细分析漏洞的原理。multipart_buffer_headers函数源码如下:

multipart_buffer_headers函数首先找boundary,如果找到boundary就执行以下代码,逐行读取请求的输入以解析body port header:

当使用get_line读入一行字符,如果该行第一个字符line[0]不是空白字符, 查找line是否存在’:’。
如果line存在字符’:’:
value指向’:’所在的内存地址。这时if(value)条件成立,成功解析到(header,value)对entry。调用zend_llist_add_element(header, &entry)存储,并使用prev_entry记录当前解析到的header,用于解析下一行。
否则,line不存在字符’:’:
认为这一行的内容是上一行解析到header对应value的值,因此进行合并。合并操作执行以下代码。

首先,为了合并value重新分配内存,接着拷贝上一行解析到的value值到新分配的内容,然后把当前行的字符串作为上一行解析到header的value值,并拷贝到value值得后面。最后调用zend_llist_remove_tail(header)删除上一行的记录。执行完后获得了新的entry,调用zend_llist_add_element(header,&entry)记录得到的header名值对(header,value)。

0x03 漏洞原理

在multipart_buffer_headers函数解析header对应value时,value值存在n行。每行的字符串以空白符开头或不存字符’:’,都触发以下合并value的代码块。那么解析header的value就要执行(n-1)次合并value的代码块。该代码块进行1次内存分配,2次内存拷贝,1次内存释放。当value值越来越长,将消耗大量的cpu时间。如果以拷贝一个字节为时间复杂度单位,value的长度为m,时间复杂度为m*m.

0x04 利用

构造像以下恶意的http请求,当存在350000行a\n时,在我的测试环境中,一个http请求将消耗10s的cpu时间。每隔若干秒,同时并发多个请求,将导致server端cpu资源长期耗尽,从而到达DOS。总的来说,利用方式和Hash Collision DOS一样。

——WebKitFormBoundarypE33TmSNWwsMphqz
Content-Disposition:form-data; name=”file”; filename=”s
a
a
a



a”
Content-Type:application/octet-stream

why is it?
——WebKitFormBoundarypE33TmSNWwsMphqz

0x05 POC

留下评论