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: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: hugh at allthethings dot co dot nz
New email:
PHP Version: OS:

 

 [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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 12:01:29 2024 UTC