mbc_to_code() out of bounds read
| Bug #72994 | mbc_to_code() out of bounds read | ||||
|---|---|---|---|---|---|
| Submitted: | 2016-09-01 13:43 UTC | Modified: | 2016-09-04 14:31 UTC | ||
| From: | research at sensepost dot com | Assigned: | cmb (profile) | ||
| Status: | Closed | Package: | mbstring related | ||
| PHP Version: | 5.6.25 | OS: | Ubuntu 14.04.4 LTS x64 | ||
| Private report: | No | CVE-ID: | None | ||
[2016-09-01 13:43 UTC] research at sensepost dot com
Description:
------------
---[SPSA-20YY-{##}/PHP]------------------------------
SECURITY ADVISORY: SPSA-2016-{##}/PHP
Affected Software: PHP 5.6.25
Vulnerability: Out of Bounds Read
CVSSv3: 5.6
Severity: Medium
Release Date: 2016-09-XX
I. Background
~~~~~~~~~~~~~
The mbc_to_code() used in mbstring extension is susceptible to a stack-based
out-of-bounds read vulnerability.
II. Description
~~~~~~~~~~~~~~~
The following test case when run on an ASAN 5.6.x build of PHP produces the following error:
<?php
$var1 = mbereg_replace($var-232338951,NULL,NULL,NULL);
var_dump($var1);
?>
==28849==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe96d960a2 at pc 0x00000061e732 bp 0x7ffe96d95770 sp 0x7ffe96d95768
READ of size 1 at 0x7ffe96d960a2 thread T0
#0 0x61e731 in mbc_to_code /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/enc/utf8.c:105
#1 0x603bd8 in fetch_token /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/regparse.c:3146
#2 0x61284d in parse_regexp /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/regparse.c:5514
#3 0x612cb4 in onig_parse_make_tree /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/regparse.c:5543
#4 0x5d8f3e in onig_compile /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/regcomp.c:5300
#5 0x5d9f46 in onig_new /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/regcomp.c:5545
#6 0x6b49b9 in php_mbregex_compile_pattern /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c:458
#7 0x6b78eb in _php_mb_regex_ereg_replace_exec /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c:870
#8 0x6bacb6 in zif_mb_ereg_replace /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c:1028
#9 0xb321d4 in zend_do_fcall_common_helper_SPEC /home/symeon/Desktop/php-5.6.25/Zend/zend_vm_execute.h:558
#10 0xb449af in ZEND_DO_FCALL_SPEC_CONST_HANDLER /home/symeon/Desktop/php-5.6.25/Zend/zend_vm_execute.h:2602
#11 0xb30495 in execute_ex /home/symeon/Desktop/php-5.6.25/Zend/zend_vm_execute.h:363
#12 0xb305de in zend_execute /home/symeon/Desktop/php-5.6.25/Zend/zend_vm_execute.h:388
#13 0xa7e9e1 in zend_execute_scripts /home/symeon/Desktop/php-5.6.25/Zend/zend.c:1341
#14 0x91951c in php_execute_script /home/symeon/Desktop/php-5.6.25/main/main.c:2613
#15 0xc9119b in do_cli /home/symeon/Desktop/php-5.6.25/sapi/cli/php_cli.c:994
#16 0xc9341f in main /home/symeon/Desktop/php-5.6.25/sapi/cli/php_cli.c:1378
#17 0x7f47551d872f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2072f)
#18 0x419e08 in _start (/home/symeon/Desktop/php-5.6.25/sapi/cli/php+0x419e08)
Address 0x7ffe96d960a2 is located in stack of thread T0 at offset 34 in frame
#0 0x6b6cac in _php_mb_regex_ereg_replace_exec /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c:788
This frame has 18 object(s):
[32, 34) 'pat_buf' <== Memory access at offset 34 overflows this variable
[96, 100) 'replace_len'
[160, 164) 'string_len'
[224, 228) 'eval'
[288, 292) 'option_str_len'
[352, 360) 'args'
[416, 424) 'arg_pattern_zval'
[480, 488) 'replace'
[544, 552) 'string'
[608, 616) 'syntax'
[672, 680) 'retval_ptr'
[736, 744) 'subpats'
[800, 824) 'out_buf'
[864, 888) 'eval_buf'
[928, 952) 'v'
[992, 1032) 'arg_replace_fci_cache'
[1088, 1160) 'arg_replace_fci'
[1216, 1306) 'err_str'
Starting with the analysis on file ext/mbstring/php_mbregex.c line 814 a
two bytes char array (pat_buf) is declared:
--- cut ---
812 OnigUChar *string_lim;
813 char *description = NULL;
814 char pat_buf[2];
--- cut ---
Setting the following breakpoints and running the PoC under the dubugger gives the following output:
gdb-peda$ b utf8.c:104
Breakpoint 1 at 0x61e6f0: file /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/enc/utf8.c, line 104.
gdb-peda$ b php_mbregex.c:863
Breakpoint 2 at 0x6b77f7: file /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c, line 863.
gdb-peda$ b utf8.c:99
Breakpoint 3 at 0x61e667: file /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/enc/utf8.c, line 99.
gdb-peda$ r ~/Desktop/php-asan_stack_oob_mbc_to_code.php
Starting program: /home/symeon/Desktop/php-5.6.25/sapi/cli/php ~/Desktop/php-asan_stack_oob_mbc_to_code.php
[Thread debugging using libthread_db enabled]
Breakpoint 2, _php_mb_regex_ereg_replace_exec (ht=0x4, return_value=0x7ffff7f5c388, return_value_ptr=0x7ffff7f21a28, this_ptr=0x0,
return_value_used=0x1, options=0x0, is_callable=0x0) at /home/symeon/Desktop/php-5.6.25/ext/mbstring/php_mbregex.c:863
863 pat_buf[0] = (char)Z_LVAL_PP(arg_pattern_zval);
gdb-peda$ n
864 pat_buf[1] = '\0';
gdb-peda$ n
866 arg_pattern = pat_buf;
gdb-peda$ print *pat_buf
$1 = 0xf9
The pat_buf[0] is the result of the Z_LVAL_PP(arg_pattern_zval) function which is 0xf9.
gdb-peda$ x/8wx pat_buf
0x7fffffff9f00: 0xffff00f9 0x00007fff 0xfffff3ee 0x00000fff
0x7fffffff9f10: 0xffff9f70 0x00007fff 0xffffa600 0x00007fff
gdb-peda$ print pat_buf[0]
$3 = 0xf9
gdb-peda$ print pat_buf[1]
$4 = 0x0
Continuing with the execution on file ext/mbstring/oniguruma/enc/utf8.c the length is
going to be calculated and then the following loop is going to be executed:
--- cut ---
99 len = enclen(ONIG_ENCODING_UTF8, p);
100 c = *p++;
101 if (len > 1) {
102 len--;
103 n = c & ((1 << (6 - len)) - 1);
104 while (len--) {
105 c = *p++;
106 n = (n << 6) | (c & ((1 << 6) - 1));
107 }
108 return n;
109 }
--- cut ---
gdb-peda$ c
Continuing.
Breakpoint 3, mbc_to_code (p=0x7fffffff9f00 <incomplete sequence \371>, end=0x7fffffff9f01 "")
at /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/enc/utf8.c:99
99 len = enclen(ONIG_ENCODING_UTF8, p);
gdb-peda$ n
100 c = *p++;
gdb-peda$ p/d len
$5 = 5
As seen above the len was incorrectly calculated. Stepping out four times,
we enter into another loop on line 104.
gdb-peda$ n
101 if (len > 1) {
gdb-peda$ n
102 len–;
gdb-peda$ n
103 n = c & ((1 << (6 – len)) – 1);
gdb-peda$ n
Breakpoint 1, mbc_to_code (p=0x7fffffff9f01 "", end=0x7fffffff9f01 "") at /home/symeon/Desktop/php-5.6.25/ext/mbstring/oniguruma/enc/utf8.c:104
104 while (len--) {
gdb-peda$ p/d len
$6 = 4
On the following executions we are going to assign the 'c' variable to the 'p' pointer
and stepping into the loop two times we end up with the following:
104 while (len--) {
gdb-peda$ n
105 c = *p++;
This time 'c' is going to be assigned with the 'p' and at this phase we started reading beyond that buffer:
gdb-peda$ print c
$7 = 0x0
gdb-peda$ print *p
$8 = 0xff
gdb-peda$ print p
$9 = (const OnigUChar *) 0x7fffffff9f02 "\377\377\377\177"
The next iteration will assign the value 0xff to the 'c' variable and thus start leaking
other variables from the stack.
Continuing the execution the out-of-bounds ASAN error kicks in:
gdb-peda$ n
=================================================================
==8169==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffff9f02 at pc 0x00000061e732 bp 0x7fffffff95d0 sp 0x7fffffff95c8
READ of size 1 at 0x7fffffff9f02 thread T0
--- cut ---
==8169==ABORTING
[Inferior 1 (process 8169) exited with code 01]
III. Impact
~~~~~~~~~~~
An attacker who successfully exploited the vulnerability could start leaking other stack variables
and may allow access to sensitive memory.
IV. Disclosure
~~~~~~~~~~~~~
Reported By: Symeon Paraschoudis
Discovery Date: 2016-08-26
Vendor Informed: 2016-09-01
Advisory Release Date: 2016-09-XX
Patch Release Date: 2016-09-XX
Advisory Updated: 2016-09-XX
---------------------------------[SPSA 2016-##/PHP]---
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2016-09-02 05:02 UTC] stas@php.net
-Type: Security +Type: Bug
[2016-09-04 14:31 UTC] cmb@php.net
-Status: Open +Status: Analyzed -Assigned To: +Assigned To: cmb
[2016-09-04 14:31 UTC] cmb@php.net
[2016-09-04 14:58 UTC] cmb@php.net
-Status: Analyzed +Status: Closed