PHP Multipart/form-data remote dos Vulnerability
catalog
1. Description 2. Analysis
?
1. Description
PHP is vulnerable to a remote denial of service, caused by repeatedly allocate memory、concatenate string、copy string and free memory when PHP parses header areas of body part of HTTP request with multipart/form-data. By sending multiple HTTP multipart requests to an affected application containing malicious header area of body part, a remote attacker could exploit this vulnerability to cause the consumption of CPU resources.
該漏洞利用PHP多次合并boundary里的參數(shù),從而造成多次內(nèi)存分配和拷貝,從而搶占CPU資源,造成性能下降。攻擊者利用并發(fā)多包的方式,可以達(dá)到使目標(biāo)系統(tǒng)拒絕服務(wù)的目的
HTTP協(xié)議中,multipart/form-data中可以包含多個(gè)報(bào)文,它們被合并在一個(gè)復(fù)雜報(bào)文中發(fā)送,每一個(gè)部分都是獨(dú)立的,以':'分隔各自的參數(shù)和值,不同部分的報(bào)文通過分界字符串(boundary)連接在一起
PHP中實(shí)現(xiàn)了解析multipart/form-data協(xié)議的功能,在解析時(shí),當(dāng)出現(xiàn)一個(gè)不包含':'的行,且之前有一個(gè)有效鍵值對,則說明該行是上一個(gè)鍵值對里的值,PHP會將值拼接到上一個(gè)鍵值對里。在拼接的過程里,PHP進(jìn)行了一次內(nèi)存分配,兩次內(nèi)存復(fù)制,以及一次內(nèi)存釋放。當(dāng)出現(xiàn)多個(gè)不包含':'的行時(shí),PHP就會進(jìn)行大量內(nèi)存分配釋放的操作,從而導(dǎo)致消耗CPU資源性能下降。短時(shí)間多次發(fā)送這類畸形請求將導(dǎo)致目標(biāo)服務(wù)器DoS
Relevant Link:
http://www.chinaz.com/news/2015/0520/407861.shtml https://portal.nsfocus.com/vulnerability/list/
?
2. Analysis
The vulnerable function is multipart_buffer_headers that is called internally by the function SAPI_POST_HANDLER_FUNC in main/rfc1867.c. SAPI_POST_HANDLER_FUNC is the entry-point function which parses body parts of HTTP request with multipart/form-data.
\php-src-master\main\rfc1867.c
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ {..while (!multipart_buffer_eof(mbuff)){char buff[FILLUNIT];char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL;size_t blen = 0, wlen = 0;zend_off_t offset;zend_llist_clean(&header);if (!multipart_buffer_headers(mbuff, &header)) {goto fileupload_done;}..
multipart_buffer_headers
/* parse headers */ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) {char *line;mime_header_entry prev_entry = {0}, entry;int prev_len, cur_len;/* didn't find boundary, abort */if (!find_boundary(self, self->boundary)) {return 0;}/* get lines of text, or CRLF_CRLF *///1. Step 1. The multipart_buffer_headers executes while loop cycle to parse current body part headers, if the boundary string was found. while( (line = get_line(self)) && line[0] != '\0' ){/*2. When parseing current body part headers which is represented as (header, value), the multipart_buffer_headers function firstly call get_line function to read a line of characters, but get_line return a line when it meets character '\n', not '\r\n'. After getting a line which is stored in the variable 'line', the multipart_buffer_headers function parses the variable line. /* add header to table PHP每次讀取HTTP body header中的一行*/char *key = line;char *value = NULL;if (php_rfc1867_encoding_translation()) {self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size);}/* space in the beginning means same header */if (!isspace(line[0])) {//尋找":"作為key-value的分界符value = strchr(line, ':');}if (value) {*value = 0;do { value++; } while(isspace(*value)); entry.value = estrdup(value);entry.key = estrdup(key); } //3. And then, it calls zend_llist_add_element function to store entryelse if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */ /*4. In this step, the multipart_buffer_headers function thinks current line is not a new header, and current line should be append to value of prev_entry. Thus, prev_entry and current line merge into a new entry by executing the following codes: */prev_len = (int)strlen(prev_entry.value);cur_len = (int)strlen(line);entry.value = emalloc(prev_len + cur_len + 1);memcpy(entry.value, prev_entry.value, prev_len);memcpy(entry.value + prev_len, line, cur_len);entry.value[cur_len + prev_len] = '\0';entry.key = estrdup(prev_entry.key);//// free memory zend_llist_remove_tail(header);} else {continue;}zend_llist_add_element(header, &entry);prev_entry = entry;}return 1; }
0x1: The Remote Denial of Service Vulnerability
1. If value of body part header consists of n lines 2. and first character of each line is not blank character 3. and each line did constains character ':'
當(dāng)滿足以上條件時(shí),multipart_buffer_headers函數(shù)會不斷進(jìn)行內(nèi)存申請、參數(shù)解析嘗試、并嘗試將當(dāng)前行解析出的參數(shù)歸并入前一個(gè)參數(shù)鍵、并釋放當(dāng)前申請內(nèi)存塊
1. executes string copy operation twice, frees memory once. 2. Each time mergence of entry.value increase length of body part header's value. 每次申請的內(nèi)存在不斷擴(kuò)大 3. thus string copy operations will cause the consumption of CPU resources, and then the service is not available. //If n is the length of body part header's value, and copying one byte is the unit time complexity,the time complexity of multipart_buffer_headers function is O(n*m)
0x2: example
------WebKitFormBoundarypE33TmSNWwsMphqz Content-Disposition: form-data; name="file"; filename="s a a a a" Content-Type: application/octet-stream <?php phpinfo();?> ------WebKitFormBoundarypE33TmSNWwsMphqz
The value of Content-Disposition consists of 5 lines, and the length of the value of Content-Disposition is 5. The multipart_buffer_headers function executes Step 2.3(內(nèi)存申請、字符串復(fù)制、內(nèi)存釋放) 4 times
1. The first time execution copys 2 bytes 2. The second execution copys 3 bytes 3. The third time execution copys 4 bytes 4. The fourth time execution copys 5 bytes 5. Thus, the multipart_buffer_headers function executes 14 times byte copy operation.
Default maximum size of body part is 2097152 bytes (2M), It is enough to cause the consumption of CPU resources by sending multiple HTTP multipart requests to an affected application containing malicious header area of body part.
Relevant Link:
https://bugs.php.net/bug.php?id=69364
?
Copyright (c) 2015 LittleHann All rights reserved
?
轉(zhuǎn)載于:https://www.cnblogs.com/LittleHann/p/5044140.html
總結(jié)
以上是生活随笔為你收集整理的PHP Multipart/form-data remote dos Vulnerability的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当开头的四字成语有哪些啊?
- 下一篇: 我让老师失去了孩子