Edit report at https://bugs.php.net/bug.php?id=74590&edit=1
ID: 74590
Updated by: cmb@php.net
Reported by: l dot wei at ntu dot edu dot sg
Summary: Heap corruption in mb_ereg_replace()
-Status: Open
+Status: Closed
Type: Bug
Package: mbstring related
Operating System: *
PHP Version: 7.1.5
-Assigned To:
+Assigned To: cmb
Block user comment: N
Private report: N
New Comment:
Both commits are contained in Oniguruma 6.1.0, and PHP-7.2 (the
lowest supported release branch) has Oniguruma 6.1.2, so this
ticket can be closed.
Previous Comments:
------------------------------------------------------------------------
[2017-06-20 06:21:07] stas@php.net
Not a security issue, and no reason to keep it private since the patches are in public.
------------------------------------------------------------------------
[2017-05-18 02:50:02] l dot wei at ntu dot edu dot sg
There are two issues in this report, which seems already fixed in the Oniguruma library, but not in
the PHP stable releases:
1) Incorrect handling of single byte char goto in parse_char_class() leads to heap corruption.
Addressed upstream by "fix invalid goto in parse_char_class() #21":
https://github.com/kkos/oniguruma/commit/1ca27efa21d3a87993af1ee80bbad7500a51b2e7
2) Incorrect handling of return value from code_to_mbclen() leads to heap corruption. 3-byte short
sequences like "\x5b\xfe\x30" or "\x5b\xff\x30" can both trigger the issue. The
code_to_mbclen() function in ext/mbstring/oniguruma/enc/utf8.c returns 1 for '\xfe' and
'\xff' as invalid code, while the return value is misinterpreted and single byte followed
by sign extension, resulting in "\xfe\xff\xff\xff" on 32-bit or
"\xfe\xff\xff\xff\xff\xff\xff\xff" on 64-bit. The 32-bit case results in heap corruption.
Seems addressed upstream by "disable USE_INVALID_CODE_SCHEME for fix #18":
https://github.com/kkos/oniguruma/commit/44ed4a8364eecdc220316a90013d2a25afea6b80
Please consider updating to the latest Oniguruma.
------------------------------------------------------------------------
[2017-05-14 07:44:10] l dot wei at ntu dot edu dot sg
Description:
------------
The multi-byte regex engine oniguruma, when handling malformed regular expressions, would write
out-of-bound, usually leading to a heap corruption. When the regex is from network, this potentially
would lead to a remote RCE.
Tested on both 5.6.30 and 7.1.5, on 32-bit and 64-bit builds in Ubuntu Linux.
The following are shown on bin/php 64-bit build with -fsanitize=address, --enable-mbstring
Some of the repros are attached.
Test script:
---------------
$ cat regex.php
<?php
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
$dir_str = dirname(__FILE__);
$file_str = ($dir_str)."/".$argv[1];
echo "Input file: ".$file_str."\n";
if (!extension_loaded('mbstring')) print "mbstring not loaded.\n";
if (!function_exists('mb_ereg_replace')) print "mb_ereg_replace() is not
available\n";
$mb_str = file_get_contents($file_str);
print strlen($mb_str) . " bytes read.\n";
echo "*** Testing mb_ereg_replace() ***\n";
echo mb_ereg_replace($mb_str, "\\1", "a\\2bxc");
?>
Expected result:
----------------
No crash.
Actual result:
--------------
A minimal repro that gives a SEGV in next_state_val() by corrupting 1-bit with a large index, on
Ubuntu x86_64:
regparse.c:4087 BITSET_SET_BIT(cc->bs, (int )(*vs));
$ xxd -g 1 fs5_id29_min
00000000: 30 5b 0b 2d 30 2d 2d 30 2d 2d 30 2d fb fb fb 30 0[.-0--0--0-...0
$ bin/php regex.php fs5_id29_min
Input file: /home/xie/php_fuzz/fs5_id29_min
16 bytes read.
*** Testing mb_ereg_replace() ***
ASAN:SIGSEGV
=================================================================
==15778==ERROR: AddressSanitizer: SEGV on unknown address 0x606000028504 (pc 0x00000084562a bp
0x7ffdb84cffb0 sp 0x7ffdb84cfe80 T0)
#0 0x845629 in next_state_val /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4087
#1 0x84b97f in parse_char_class /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4304
#2 0x85511b in parse_exp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5270
#3 0x8589a5 in parse_branch /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5445
#4 0x858c2d in parse_subexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5472
#5 0x8593ef in parse_regexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5516
#6 0x8593ef in onig_parse_make_tree /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5543
#7 0x82de6e in onig_compile /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5300
#8 0x82f8e5 in onig_new /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5545
#9 0x8c50a0 in php_mbregex_compile_pattern /home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:456
#10 0x8c637b in _php_mb_regex_ereg_replace_exec
/home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:901
#11 0xda5c21 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER
/home/xie/php-7.1.5/Zend/zend_vm_execute.h:675
#12 0xd5f93d in execute_ex /home/xie/php-7.1.5/Zend/zend_vm_execute.h:429
#13 0xec5c91 in zend_execute /home/xie/php-7.1.5/Zend/zend_vm_execute.h:474
#14 0xc7dd12 in zend_execute_scripts /home/xie/php-7.1.5/Zend/zend.c:1476
#15 0xb8044f in php_execute_script /home/xie/php-7.1.5/main/main.c:2537
#16 0xeca7d0 in do_cli /home/xie/php-7.1.5/sapi/cli/php_cli.c:993
#17 0x43ef86 in main /home/xie/php-7.1.5/sapi/cli/php_cli.c:1381
#18 0x7f7a9755082f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#19 0x43f228 in _start (/home/xie/php7_asan/bin/php+0x43f228)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4087
next_state_val
==15778==ABORTING
A few other variations:
$ xxd -g 1 fs6_id33_min
00000000: 5b 30 2d fc 30 30 30 30 30 2d c4 30 [0-.00000-.0
$ bin/php regex.php fs6_id33_min
Input file: /home/xie/php_fuzz/fs6_id33_min
12 bytes read.
*** Testing mb_ereg_replace() ***
ASAN:SIGSEGV
=================================================================
==15781==ERROR: AddressSanitizer: SEGV on unknown address 0x2c047fff97a2 (pc 0x0000008446e0 bp
0x00000130e0e4 sp 0x7ffe308b8920 T0)
#0 0x8446df in bbuf_free /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:104
#1 0x84ae9a in onig_node_free /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:1048
#2 0x84b3bf in parse_char_class /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4497
#3 0x85511b in parse_exp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5270
#4 0x85890d in parse_branch /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5435
#5 0x858c2d in parse_subexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5472
#6 0x8593ef in parse_regexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5516
#7 0x8593ef in onig_parse_make_tree /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5543
#8 0x82de6e in onig_compile /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5300
#9 0x82f8e5 in onig_new /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5545
#10 0x8c50a0 in php_mbregex_compile_pattern /home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:456
#11 0x8c637b in _php_mb_regex_ereg_replace_exec
/home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:901
#12 0xda5c21 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER
/home/xie/php-7.1.5/Zend/zend_vm_execute.h:675
#13 0xd5f93d in execute_ex /home/xie/php-7.1.5/Zend/zend_vm_execute.h:429
#14 0xec5c91 in zend_execute /home/xie/php-7.1.5/Zend/zend_vm_execute.h:474
#15 0xc7dd12 in zend_execute_scripts /home/xie/php-7.1.5/Zend/zend.c:1476
#16 0xb8044f in php_execute_script /home/xie/php-7.1.5/main/main.c:2537
#17 0xeca7d0 in do_cli /home/xie/php-7.1.5/sapi/cli/php_cli.c:993
#18 0x43ef86 in main /home/xie/php-7.1.5/sapi/cli/php_cli.c:1381
#19 0x7fcfe9c5582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#20 0x43f228 in _start (/home/xie/php7_asan/bin/php+0x43f228)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:104 bbuf_free
==15781==ABORTING
Other variations (repros can be uploaded on request).
$ bin/php regex.php fs5_id29
Input file: /home/xie/php_fuzz/fs5_id29
1245 bytes read.
*** Testing mb_ereg_replace() ***
=================================================================
==15901==ERROR: AddressSanitizer: heap-use-after-free on address 0x60600000bc30 at pc 0x00000084533a
bp 0x7fff88fbfef0 sp 0x7fff88fbfee0
READ of size 8 at 0x60600000bc30 thread T0
#0 0x845339 in add_code_range_to_buf /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:1716
#1 0x84566d in add_code_range /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:1781
#2 0x84566d in next_state_val /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4089
#3 0x84b97f in parse_char_class /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4304
#4 0x84b480 in parse_char_class /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4402
#5 0x84b480 in parse_char_class /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4402
#6 0x85511b in parse_exp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5270
#7 0x85890d in parse_branch /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5435
#8 0x858c2d in parse_subexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5472
#9 0x855f90 in parse_enclose /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:4730
#10 0x855f90 in parse_exp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5057
#11 0x85890d in parse_branch /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5435
#12 0x858c2d in parse_subexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5472
#13 0x8593ef in parse_regexp /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5516
#14 0x8593ef in onig_parse_make_tree /home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:5543
#15 0x82de6e in onig_compile /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5300
#16 0x82f8e5 in onig_new /home/xie/php-7.1.5/ext/mbstring/oniguruma/regcomp.c:5545
#17 0x8c50a0 in php_mbregex_compile_pattern /home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:456
#18 0x8c637b in _php_mb_regex_ereg_replace_exec
/home/xie/php-7.1.5/ext/mbstring/php_mbregex.c:901
#19 0xda5c21 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER
/home/xie/php-7.1.5/Zend/zend_vm_execute.h:675
#20 0xd5f93d in execute_ex /home/xie/php-7.1.5/Zend/zend_vm_execute.h:429
#21 0xec5c91 in zend_execute /home/xie/php-7.1.5/Zend/zend_vm_execute.h:474
#22 0xc7dd12 in zend_execute_scripts /home/xie/php-7.1.5/Zend/zend.c:1476
#23 0xb8044f in php_execute_script /home/xie/php-7.1.5/main/main.c:2537
#24 0xeca7d0 in do_cli /home/xie/php-7.1.5/sapi/cli/php_cli.c:993
#25 0x43ef86 in main /home/xie/php-7.1.5/sapi/cli/php_cli.c:1381
#26 0x7ff18d2a682f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#27 0x43f228 in _start (/home/xie/php7_asan/bin/php+0x43f228)
0x60600000bc30 is located 48 bytes inside of 56-byte region [0x60600000bc00,0x60600000bc38)
freed by thread T0 here:
#0 0x7ff18e1ca2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
#1 0xcfd166 in zend_string_release /home/xie/php-7.1.5/Zend/zend_string.h:272
#2 0xcfd166 in zend_new_interned_string_int /home/xie/php-7.1.5/Zend/zend_string.c:148
previously allocated by thread T0 here:
#0 0x7ff18e1ca602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0xc05d88 in __zend_malloc /home/xie/php-7.1.5/Zend/zend_alloc.c:2820
#2 0x1a3b9df (/home/xie/php7_asan/bin/php+0x1a3b9df)
SUMMARY: AddressSanitizer: heap-use-after-free
/home/xie/php-7.1.5/ext/mbstring/oniguruma/regparse.c:1716 add_code_range_to_buf
Shadow bytes around the buggy address:
0x0c0c7fff9730: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c7fff9740: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
0x0c0c7fff9750: fd fd fd fd fd fd fd fa fa fa fa fa 00 00 00 00
0x0c0c7fff9760: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c7fff9770: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
=>0x0c0c7fff9780: fd fd fd fd fd fd[fd]fa fa fa fa fa 00 00 00 00
0x0c0c7fff9790: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c7fff97a0: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
0x0c0c7fff97b0: fd fd fd fd fd fd fd fa fa fa fa fa 00 00 00 00
0x0c0c7fff97c0: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c7fff97d0: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
==15901==ABORTING
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=74590&edit=1