php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72823 strtr out-of-bound access
Submitted: 2016-08-12 18:45 UTC Modified: 2016-08-13 10:03 UTC
From: fernando at null-life dot com Assigned: cmb
Status: Closed Package: Strings related
PHP Version: 5.6.24 OS: *
Private report: No CVE-ID:
 [2016-08-12 18:45 UTC] fernando at null-life dot com
Description:
------------
php_strtr_hash will read out if bounds if the pattern length is bigger than the frist argument to strtr, this can be seen easier when the first argument is casted to string instead of passed as a regular string.

-----------------------------------------

Source code  

https://github.com/php/php-src/blob/PHP-5.6.24/ext/standard/string.c#L2853

static inline HASH php_strtr_hash(const char *str, int len)
{
    HASH	res = 0;
    int		i;
    for (i = 0; i < len; i++) {
        res = res * 33 + (unsigned char)str[i];   // str[i] read out of bounds
    }

    return res;
}

GDB Output
-----------

USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 gdb -q --args /home/operac/build2-sinafl/bin/php -n poc.php
Reading symbols from /home/operac/build2-sinafl/bin/php...done.
gdb-peda$ b string.c:3068
Breakpoint 2 at 0xcdf293: file /home/operac/build2-sinafl/php-src-56/ext/standard/string.c, line 3068.
gdb-peda$ r
...
Breakpoint 2, php_strtr_array_do_repl (text=0x7fffffffa210, d=0x606000001100, return_value=0x60300004cd50) at /home/operac/build2-sinafl/php-src-56/ext/standard/string.c:3068
3068                    HASH    h               = php_strtr_hash(&S(text)[pos + d->m - d->B], d->B) & d->shift->table_mask;
gdb-peda$ p *text
$6 = {
  s = 0x607000089af0 "11",
  l = 0x2
}
gdb-peda$ p/d pos + d->m - d->B
$7 = -3                           // text underflow
gdb-peda$ b string.c:2855
Breakpoint 3 at 0xcdd3ce: file /home/operac/build2-sinafl/php-src-56/ext/standard/string.c, line 2855.
gdb-peda$ c
Continuing.
...

Breakpoint 3, php_strtr_hash (str=0x607000089aed "", len=0x2) at /home/operac/build2-sinafl/php-src-56/ext/standard/string.c:2855
2855                    res = res * 33 + (unsigned char)str[i];
gdb-peda$ p str
$9 = 0x607000089aed ""
gdb-peda$ p str+3
$10 = 0x607000089af0 "11"

...


Test script:
---------------
<?php

$d = array("aaa" => "bbb");
strtr(11, $d);

Expected result:
----------------
No crash

Actual result:
--------------
ASan output:

USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 /home/operac/build2/bin/php -n poc.php

=================================================================
==30419==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x607000089b5d at pc 0x00000147f3e1 bp 0x7ffe4de13900 sp 0x7ffe4de138f0
READ of size 1 at 0x607000089b5d thread T0
    #0 0x147f3e0 in php_strtr_hash /home/operac/build2/php-src-56/ext/standard/string.c:2855
    #1 0x147f3e0 in php_strtr_array_do_repl /home/operac/build2/php-src-56/ext/standard/string.c:3068
    #2 0x147f3e0 in php_strtr_array /home/operac/build2/php-src-56/ext/standard/string.c:3136
    #3 0x147f3e0 in zif_strtr /home/operac/build2/php-src-56/ext/standard/string.c:3167
    #4 0x1d5c3e3 in zend_do_fcall_common_helper_SPEC /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:558
    #5 0x1c0568c in execute_ex /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:363
    #6 0x194d3d2 in zend_execute_scripts /home/operac/build2/php-src-56/Zend/zend.c:1341
    #7 0x169b32f in php_execute_script /home/operac/build2/php-src-56/main/main.c:2613
    #8 0x1d653b6 in do_cli /home/operac/build2/php-src-56/sapi/cli/php_cli.c:994
    #9 0x4550a0 in main /home/operac/build2/php-src-56/sapi/cli/php_cli.c:1378
    #10 0x7fbefc95f82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #11 0x4556b8 in _start (/home/operac/build2/bin/php+0x4556b8)

0x607000089b5d is located 3 bytes to the left of 79-byte region [0x607000089b60,0x607000089baf)
allocated by thread T0 here:
    #0 0x7fbefef29961 in realloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98961)
    #1 0x16afa27 in xbuf_format_converter /home/operac/build2/php-src-56/main/spprintf.c:794
    #2 0x16b7361 in vspprintf /home/operac/build2/php-src-56/main/spprintf.c:821
    #3 0x1a0d2e9 in zend_spprintf /home/operac/build2/php-src-56/Zend/zend_exceptions.c:663
    #4 0x18faf81 in _convert_to_string /home/operac/build2/php-src-56/Zend/zend_operators.c:625
    #5 0x1980f2f in zend_parse_arg_impl /home/operac/build2/php-src-56/Zend/zend_API.c:442
    #6 0x198881d in zend_parse_arg /home/operac/build2/php-src-56/Zend/zend_API.c:691
    #7 0x198b035 in zend_parse_va_args /home/operac/build2/php-src-56/Zend/zend_API.c:873
    #8 0x198b035 in zend_parse_parameters /home/operac/build2/php-src-56/Zend/zend_API.c:924
    #9 0x1479b66 in zif_strtr /home/operac/build2/php-src-56/ext/standard/string.c:3152
    #10 0x1d5c3e3 in zend_do_fcall_common_helper_SPEC /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:558
    #11 0x1c0568c in execute_ex /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:363
    #12 0x194d3d2 in zend_execute_scripts /home/operac/build2/php-src-56/Zend/zend.c:1341
    #13 0x169b32f in php_execute_script /home/operac/build2/php-src-56/main/main.c:2613
    #14 0x1d653b6 in do_cli /home/operac/build2/php-src-56/sapi/cli/php_cli.c:994
    #15 0x4550a0 in main /home/operac/build2/php-src-56/sapi/cli/php_cli.c:1378
    #16 0x7fbefc95f82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/operac/build2/php-src-56/ext/standard/string.c:2855 php_strtr_hash


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-12 21:42 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-08-13 09:22 UTC] cmb@php.net
-Status: Open +Status: Analyzed -Assigned To: +Assigned To: cmb
 [2016-08-13 09:22 UTC] cmb@php.net
I can confirm this issue.

> p/d pos + d->m - d->B ==> -3

This happens in php_strtr_array_prepare()[1], because patnum is 0
in this case. Unfortunately, the assert doesn't catch this case.
Anyhow, the solution is to not call php_strtr_array_prepare()[2],
if patterns_len is 0, but to return the given str instead.

[1] <https://github.com/php/php-src/blob/PHP-5.6/ext/standard/string.c#L2986-L2992>
[2] <https://github.com/php/php-src/blob/PHP-5.6/ext/standard/string.c#L3134>
 [2016-08-13 10:03 UTC] cmb@php.net
For the record: PHP 7 is not affected, as strtr() had been
reimplemented with
<https://github.com/php/php-src/commit/b1ff1527>.
 [2016-08-13 10:19 UTC] cmb@php.net
Automatic comment on behalf of cmbecker69@gmx.de
Revision: http://git.php.net/?p=php-src.git;a=commit;h=ae3b2078eac226c61bc325527e607ad275936d22
Log: Fix #72823: strtr out-of-bound access
 [2016-08-13 10:19 UTC] cmb@php.net
-Status: Analyzed +Status: Closed
 [2016-10-17 10:09 UTC] bwoebi@php.net
Automatic comment on behalf of cmbecker69@gmx.de
Revision: http://git.php.net/?p=php-src.git;a=commit;h=ae3b2078eac226c61bc325527e607ad275936d22
Log: Fix #72823: strtr out-of-bound access
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Wed Jun 28 12:01:42 2017 UTC