php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #74590 Heap corruption in mb_ereg_replace()
Submitted: 2017-05-14 07:44 UTC Modified: 2020-03-05 09:15 UTC
From: l dot wei at ntu dot edu dot sg Assigned: cmb (profile)
Status: Closed Package: mbstring related
PHP Version: 7.1.5 OS: *
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: l dot wei at ntu dot edu dot sg
New email:
PHP Version: OS:

 

 [2017-05-14 07:44 UTC] 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





Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-05-18 02:50 UTC] 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-06-20 06:21 UTC] stas@php.net
-Type: Security +Type: Bug -Assigned To: +Assigned To: hirokawa
 [2017-06-20 06:21 UTC] stas@php.net
Not a security issue, and no reason to keep it private since the patches are in public.
 [2017-10-24 07:00 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: hirokawa +Assigned To:
 [2020-03-05 09:15 UTC] cmb@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: cmb
 [2020-03-05 09:15 UTC] cmb@php.net
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.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 22:01:28 2024 UTC