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

================= Bug Report (1/1) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=76557
## Description:
PHP Bug ID: 76557
Summary: heap-buffer-overflow (READ of size 48) while reading exif data
Status: Closed
PHP Version: 5.6.36
Assigned: kalle (profile)
CVE-ID: 2018-14851

Description:
Description:
------------
USE_ZEND_ALLOC=0 ./php-7.2.7 -r '$exif = exif_read_data("http://dtf.pw/php727/poc/630/test000.jpeg"); var_dump($exif);'


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

Actual result:
--------------
==996==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d000011958 at pc 0x0000004ce426 bp 0x7ffc064d6a00 sp 0x7ffc064d61b0
READ of size 48 at 0x61d000011958 thread T0
    #0 0x4ce425 in __asan_memcpy /b/build/slave/linux_upload_clang/build/src/third_party/llvm/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23:3
    #1 0x13f4905 in _estrndup /root/php-7.2.7/Zend/zend_alloc.c:2538:2
    #2 0xe039ad in exif_iif_add_value /root/php-7.2.7/ext/exif/exif.c:2119:21
    #3 0xe039ad in exif_iif_add_tag /root/php-7.2.7/ext/exif/exif.c:2199
    #4 0xe0b818 in exif_process_IFD_TAG /root/php-7.2.7/ext/exif/exif.c:3543:2
    #5 0xe0bccf in exif_process_IFD_in_MAKERNOTE /root/php-7.2.7/ext/exif/exif.c:3213:8
    #6 0xe0bccf in exif_process_IFD_TAG /root/php-7.2.7/ext/exif/exif.c:3494
    #7 0xe08c15 in exif_process_IFD_in_JPEG /root/php-7.2.7/ext/exif/exif.c:3576:8
    #8 0xe0ac0e in exif_process_IFD_TAG /root/php-7.2.7/ext/exif/exif.c:3534:11
    #9 0xe08c15 in exif_process_IFD_in_JPEG /root/php-7.2.7/ext/exif/exif.c:3576:8
    #10 0xe014c0 in exif_process_TIFF_in_JPEG /root/php-7.2.7/ext/exif/exif.c:3665:2
    #11 0xe014c0 in exif_process_APP1 /root/php-7.2.7/ext/exif/exif.c:3690
    #12 0xe014c0 in exif_scan_JPEG_header /root/php-7.2.7/ext/exif/exif.c:3835
    #13 0xe014c0 in exif_scan_FILE_header /root/php-7.2.7/ext/exif/exif.c:4224
    #14 0xe014c0 in exif_read_from_impl /root/php-7.2.7/ext/exif/exif.c:4365
    #15 0xe014c0 in exif_read_from_stream /root/php-7.2.7/ext/exif/exif.c:4382
    #16 0xdf8f18 in exif_read_from_file /root/php-7.2.7/ext/exif/exif.c:4409:8
    #17 0xdf8f18 in zif_exif_read_data /root/php-7.2.7/ext/exif/exif.c:4482
    #18 0x17c5d34 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /root/php-7.2.7/Zend/zend_vm_execute.h:617:2
    #19 0x15ed419 in execute_ex /root/php-7.2.7/Zend/zend_vm_execute.h:59723:7
    #20 0x15eda9a in zend_execute /root/php-7.2.7/Zend/zend_vm_execute.h:63760:2
    #21 0x14758eb in zend_eval_stringl /root/php-7.2.7/Zend/zend_execute_API.c:1082:4
    #22 0x1475fb9 in zend_eval_stringl_ex /root/php-7.2.7/Zend/zend_execute_API.c:1123:11
    #23 0x1475fb9 in zend_eval_string_ex /root/php-7.2.7/Zend/zend_execute_API.c:1134
    #24 0x18c4aea in do_cli /root/php-7.2.7/sapi/cli/php_cli.c:1044:8
    #25 0x18c2c03 in main /root/php-7.2.7/sapi/cli/php_cli.c:1405:18
    #26 0x7f43ac6d32e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0)
    #27 0x427479 in _start (/root/php-7.2.7/sapi/cli/php+0x427479)

Address 0x61d000011958 is a wild pointer.
SUMMARY: AddressSanitizer: heap-buffer-overflow /b/build/slave/linux_upload_clang/build/src/third_party/llvm/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23:3 in __asan_memcpy

Comments:

[Comment 1] [2018-07-01 17:03 UTC] cmb@php.net
Thanks for reporting this issue!  I can confirm the OOB read with
valgrind on PHP-7.1 and master (haven't tested other versions).

[Comment 2] [2018-07-02 04:20 UTC] stas@php.net
I am not sure whether I understand the code right or not, but it looks to me like the problem is in exif_process_IFD_in_MAKERNOTE, where the code changes offset_base:

 switch (maker_note->offset_mode) {
  case MN_OFFSET_MAKER:
   offset_base = value_ptr;

... etc.

When offset_base is changed, both IFDlength and displacement stay the same, so when the values in exif_process_IFD_TAG() are verified:

  if (byte_count > IFDlength || offset_val > IFDlength-byte_count || value_ptr < dir_entry || offset_val < (size_t)(dir_entry-offset_base)) {

they are checked against the same IFDlength values as before, however the code uses new offset_base:

  value_ptr = offset_base+offset_val;

So if that offset_base is more than before, and offset_val is checked the length measured with old base, the new value can cause reading outside the data that is loaded.

[Comment 3] [2018-07-02 05:22 UTC] stas@php.net
Proposed fix in https://gist.github.com/smalyshev/9e3197a51b489ab0ecb2438da6f4d59f and in security repo as 53cb7bf758cb1137239b069c5642ac00736bf787. Please verify.
Links: https://gist.github.com/smalyshev/9e3197a51b489ab0ecb2438da6f4d59f

[Comment 4] [2018-07-02 05:26 UTC] stas@php.net
Related To: Bug #76558
Links: https://bugs.php.net/bug.php?id=76558

[Comment 5] [2018-07-02 14:02 UTC] geeknik at protonmail dot ch
I can confirm the proposed patch fixes this issue (and #76558) in PHP 7.2.7.

[Comment 7] [2018-08-02 19:00 UTC] geeknik at protonmail dot ch
CVE-2018-14851 has been assigned to this bug.

Commit References:
53cb7bf758cb1137239b069c5642ac00736bf787

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 b52c4fcbcd0c07858161cb159c21a945f1a1e47f
WORKDIR $SRC/php-src
COPY build.sh $SRC/

Additional files: