php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #70385 Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes
Submitted: 2015-08-28 18:43 UTC Modified: 2015-09-01 18:44 UTC
From: hugh at allthethings dot co dot nz Assigned:
Status: Closed Package: EXIF related
PHP Version: 5.6.13RC1 OS: Linux
Private report: No CVE-ID:
 [2015-08-28 18:43 UTC] hugh at allthethings dot co dot nz
Description:
------------
When supplying a TIFF file that has a tag for one of Make, Model, or Copyright, where the value is exactly 32 bytes (with no null bytes), a buffer over-read occurs. This will cause a crash to happen when compiled with ASAN, or potential data leak when not.

The TIFF file that resulted in this bug was found using the american fuzzy-lop (afl) fuzzer. Compiled with ./configure --enable-exif --enable-static --enable-cli --disable-shared --disable-all, with CC of afl-gcc, or if wanting to reproduce without, with CC of gcc, and CFLAGS of -fsanitize=address. Running the test script with the tiff file available at https://transfer.sh/VOhn6/fuzz.tiff

The hexdump of file in question is shown below:

00000000  49 49 2a 00 62 00 00 00  30 30 30 30 30 30 30 30  |II*.b...00000000|
00000010  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
00000060  30 30 03 00 0f 01 30 30  20 00 00 00 30 00 00 00  |00....00 ...0...|
00000070  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
0000008c

The overread happens in the function _estrdup in Zend/zend_alloc.c line 2630 when the call to strlen happens then again on line 2636 when the call to memcpy happens. The cause of this is in the function exif_process_IFD_TAG in ext/exif/exif.c line 2866 (if (byte_count>sizeof(cbuf)) {), where the > should be a >=. This allows a 32 byte string from the TIFF file to be read into the static array cbuf with no trailing null byte, so any string operations read past the buffer.

I've attached a patch, and I'm working on a POC exploit for this, but just getting the initial bug report done!

Test script:
---------------
<?php
exif_read_data($argv[1]);
?>

Expected result:
----------------
No crash when compiled with ASAN, and no buffer overread when not. A new buffer should be created when value is 32 bytes long, so a 33rd character has a null byte on the end.

Actual result:
--------------
Warning: exif_read_data(fuzz.tiff): Process tag(x010F=Make       ): Illegal format code 0x3030, suppose BYTE in /root/fuzzing/php/exif.php on line 2
=================================================================
==24169== ERROR: AddressSanitizer: unknown-crash on address 0x7fffffff99e0 at pc 0x7ffff4e5a321 bp 0x7fffffff9830 sp 0x7fffffff9808
READ of size 37 at 0x7fffffff99e0 thread T0
    #0 0x7ffff4e5a320 (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0xf320)
    #1 0xf5b5c4 (/root/php-src/sapi/cli/php+0xf5b5c4)
    #2 0x805ee2 (/root/php-src/sapi/cli/php+0x805ee2)
    #3 0x80c2c5 (/root/php-src/sapi/cli/php+0x80c2c5)
    #4 0x81c9a8 (/root/php-src/sapi/cli/php+0x81c9a8)
    #5 0x8252f8 (/root/php-src/sapi/cli/php+0x8252f8)
    #6 0x1775bc5 (/root/php-src/sapi/cli/php+0x1775bc5)
    #7 0x132fcf0 (/root/php-src/sapi/cli/php+0x132fcf0)
    #8 0x1117670 (/root/php-src/sapi/cli/php+0x1117670)
    #9 0xd66dc1 (/root/php-src/sapi/cli/php+0xd66dc1)
    #10 0x1785466 (/root/php-src/sapi/cli/php+0x1785466)
    #11 0x434ce8 (/root/php-src/sapi/cli/php+0x434ce8)
    #12 0x7ffff4382ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
    #13 0x435ec5 (/root/php-src/sapi/cli/php+0x435ec5)
Address 0x7fffffff99e0 is located at offset 32 in frame <exif_process_IFD_TAG> of T0's stack:
  This frame has 3 object(s):
    [32, 64) 'cbuf'
    [96, 128) 'tmp'
    [160, 224) 'tagname'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
  0x10007fff72e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff72f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007fff7330: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[00]00 00 00
  0x10007fff7340: f2 f2 f2 f2 00 00 00 00 f2 f2 f2 f2 00 00 00 00
  0x10007fff7350: 00 00 00 00 f3 f3 f3 f3 00 00 00 00 00 00 00 00
  0x10007fff7360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7380: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00
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
==24169== ABORTING
(gdb) bt
#0  0x00007ffff4397cc9 in __GI_raise (sig=sig@entry=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007ffff439b0d8 in __GI_abort () at abort.c:89
#2  0x00007ffff4e66829 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#3  0x00007ffff4e5d3ec in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#4  0x00007ffff4e64012 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#5  0x00007ffff4e63121 in __asan_report_error ()
   from /usr/lib/x86_64-linux-gnu/libasan.so.0
#6  0x00007ffff4e5a339 in strlen () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#7  0x0000000000f5b5c5 in _estrdup (
    s=s@entry=0x7fffffff99e0 '0' <repeats 32 times>, "\263\212\265A")
    at /root/php-src/Zend/zend_alloc.c:2630
#8  0x0000000000805ee3 in exif_process_IFD_TAG (ImageInfo=0x7fffffffa2e0, 
    dir_entry=<optimized out>, offset_base=<optimized out>, 
    IFDlength=<optimized out>, displacement=0, section_index=3, ReadNextIFD=0, 
    tag_table=0x18efe80 <tag_table_IFD>) at /root/php-src/ext/exif/exif.c:3054
#9  0x000000000080c2c6 in exif_process_IFD_in_TIFF (
    ImageInfo=ImageInfo@entry=0x7fffffffa2e0, dir_offset=98, 
    section_index=section_index@entry=3) at /root/php-src/ext/exif/exif.c:3700
#10 0x000000000081c9a9 in exif_scan_FILE_header (ImageInfo=0x7fffffffa2e0)
    at /root/php-src/ext/exif/exif.c:3794
#11 exif_read_file (ImageInfo=ImageInfo@entry=0x7fffffffa2e0, 
    FileName=<optimized out>, read_thumbnail=<optimized out>, read_all=0)
    at /root/php-src/ext/exif/exif.c:3903
#12 0x00000000008252f9 in zif_exif_read_data (ht=<optimized out>, 
    return_value=0x7ffff7f963c8, return_value_ptr=<optimized out>, 
    this_ptr=<optimized out>, return_value_used=<optimized out>)
    at /root/php-src/ext/exif/exif.c:3956
#13 0x0000000001775bc6 in zend_do_fcall_common_helper_SPEC (
    execute_data=0x7ffff7f63940) at /root/php-src/Zend/zend_vm_execute.h:558
#14 0x000000000132fcf1 in execute_ex (execute_data=0x7ffff7f63940)
    at /root/php-src/Zend/zend_vm_execute.h:363
#15 0x0000000001117671 in zend_execute_scripts (type=type@entry=8, 
    retval=retval@entry=0x0, file_count=file_count@entry=3)
    at /root/php-src/Zend/zend.c:1341
#16 0x0000000000d66dc2 in php_execute_script (
    primary_file=primary_file@entry=0x7fffffffd2d0)
    at /root/php-src/main/main.c:2597
#17 0x0000000001785467 in do_cli (argc=3, argv=0x60060000ecb0)
    at /root/php-src/sapi/cli/php_cli.c:994
#18 0x0000000000434ce9 in main (argc=3, argv=0x60060000ecb0)
    at /root/php-src/sapi/cli/php_cli.c:1378

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-08-28 18:45 UTC] hugh at allthethings dot co dot nz
OK, so can't attach a patch, but it is at https://transfer.sh/aFCP3/exif.patch
 [2015-08-29 05:21 UTC] stas@php.net
I am not sure how the proposed patch fixes the problem - if the buffer allocated is byte_count size and byte_count bytes are read, then nothing guarantees this buffer is null-terminated. As such, probably the proper patch would be to replace estrdup with estrndup. In fact, with the >= patch the issue is easier to detect as it shows on valgrind (with > it does not :)
 [2015-08-29 05:26 UTC] stas@php.net
Please try this patch: https://gist.github.com/smalyshev/396198560b551ba5cbb2
 [2015-08-29 06:13 UTC] hugh at allthethings dot co dot nz
Can't test patch right now but I notice that you don't change the strdups for copyright as well. Will test in the next few days.
 [2015-08-31 06:10 UTC] hugh at allthethings dot co dot nz
Yup, I can still reproduce that bug with the tiff file at https://transfer.sh/11RTrT/exif-crash-copyright.tiff which makes use of the copyright tag. The amended patch at https://transfer.sh/aiaTm/exif-2.patch resolves that.

Cheers,

Hugh
 [2015-08-31 06:19 UTC] stas@php.net
Thanks, I have amended the patch accordingly.
 [2015-09-01 08:16 UTC] hugh at allthethings dot co dot nz
Cool, will this bug get a CVE assigned?

Cheers,

Hugh
 [2015-09-01 18:55 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=64043cb9e5d8bc5af719678893e38ee0290e0c0a
Log: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes)
 [2015-09-01 18:55 UTC] stas@php.net
-Status: Open +Status: Closed
 [2015-09-01 19:04 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=64043cb9e5d8bc5af719678893e38ee0290e0c0a
Log: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes)
 [2015-09-01 19:07 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=64043cb9e5d8bc5af719678893e38ee0290e0c0a
Log: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes)
 [2015-09-02 08:29 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=64043cb9e5d8bc5af719678893e38ee0290e0c0a
Log: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes)
 [2015-09-03 18:10 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=64043cb9e5d8bc5af719678893e38ee0290e0c0a
Log: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes)
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Mar 28 08:01:44 2017 UTC