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里的參數,從而造成多次內存分配和拷貝,從而搶占CPU資源,造成性能下降。攻擊者利用并發多包的方式,可以達到使目標系統拒絕服務的目的
HTTP協議中,multipart/form-data中可以包含多個報文,它們被合并在一個復雜報文中發送,每一個部分都是獨立的,以':'分隔各自的參數和值,不同部分的報文通過分界字符串(boundary)連接在一起
PHP中實現了解析multipart/form-data協議的功能,在解析時,當出現一個不包含':'的行,且之前有一個有效鍵值對,則說明該行是上一個鍵值對里的值,PHP會將值拼接到上一個鍵值對里。在拼接的過程里,PHP進行了一次內存分配,兩次內存復制,以及一次內存釋放。當出現多個不包含':'的行時,PHP就會進行大量內存分配釋放的操作,從而導致消耗CPU資源性能下降。短時間多次發送這類畸形請求將導致目標服務器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 ':'
當滿足以上條件時,multipart_buffer_headers函數會不斷進行內存申請、參數解析嘗試、并嘗試將當前行解析出的參數歸并入前一個參數鍵、并釋放當前申請內存塊
1. executes string copy operation twice, frees memory once. 2. Each time mergence of entry.value increase length of body part header's value. 每次申請的內存在不斷擴大 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(內存申請、字符串復制、內存釋放) 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
?
轉載于:https://www.cnblogs.com/LittleHann/p/5044140.html
總結
以上是生活随笔為你收集整理的PHP Multipart/form-data remote dos Vulnerability的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当开头的四字成语有哪些啊?
- 下一篇: 我让老师失去了孩子