php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #71527 Buffer over-write in finfo_open with malformed magic file.
Submitted: 2016-02-04 22:48 UTC Modified: 2016-04-25 17:08 UTC
From: hugh at allthethings dot co dot nz Assigned: kaplan
Status: Closed Package: Reproducible crash
PHP Version: 5.6.18 OS: Linux
Private report: No CVE-ID: 2015-8865
 [2016-02-04 22:48 UTC] hugh at allthethings dot co dot nz
Description:
------------
Found this using afl-fuzz, see http://lcamtuf.coredump.cx/afl/

This bug causes a segfault when running with environment variable USE_ZEND_ALLOC set to 0, and also when compiled with ASAN with USE_ZEND_ALLOC set and unset.

To reproduce, run the following PHP file, with the example magic file below.

$ cat magic-open.php
<?php
$finfo = finfo_open(FILEINFO_NONE, $argv[1]);
$info = finfo_file($finfo, $argv[2]);
var_dump($info);
?>

Magic file is (used without ASAN):
$ xxd -g 1 magic.crash-noasan
0000000: 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e  >>>>>>>>>>>>>>>>
0000010: 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e     >>>>>>>>>>>>>>>

$ cat magic.crash-noasan
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Magic file is (used with ASAN):
$ xxd -g 1 magic.crash-asan
0000000: 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e  >>>>>>>>>>>>>>>>
0000010: 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e 3e  >>>>>>>>>>>>>>>>
0000020: 71 3e 3e 3e 3e 3e 3e 3e 3e 0a 00                 q>>>>>>>>..

$ cat magic.crash-asan
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>q>>>>>>>>

Then run the program like:

./sapi/cli/php magic-open.php magic.crash /dev/null

You will get the following when NOT compiled with ASAN, and USE_ZEND_ALLOC is UNSET (no crash).

$ ./php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null

Warning: finfo_open(): Failed to load magic database at '/root/php-src/magic.crash-noasan'. in /root/php-src/magic-open.php on line 2

Warning: finfo_file() expects parameter 1 to be resource, boolean given in /root/php-src/magic-open.php on line 3
bool(false)


You will get the following when NOT compiled with ASAN, and USE_ZEND_ALLOC is set to 0 (crash).

 $ USE_ZEND_ALLOC=0 ./php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null
Segmentation fault

 $ USE_ZEND_ALLOC=0 gdb --args ./php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null                                                                      
<snip>
(gdb) r
Starting program: /root/php-src/php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null

Program received signal SIGSEGV, Segmentation fault.
_int_malloc (av=0x7ffff76ae760 <main_arena>, bytes=79) at malloc.c:3489
3489    malloc.c: No such file or directory.
(gdb) bt
#0  _int_malloc (av=0x7ffff76ae760 <main_arena>, bytes=79) at malloc.c:3489
#1  0x00007ffff73727b0 in __GI___libc_malloc (bytes=79) at malloc.c:2891
#2  0x0000000000a9fb44 in xbuf_format_converter (xbuf=xbuf@entry=0x7fffffff9d30, fmt=fmt@entry=0x1188930 "Failed to load magic database at '%s'.", 
    ap=ap@entry=0x7fffffff9e30) at /root/php-src/main/spprintf.c:245
#3  0x0000000000aa260d in vspprintf (pbuf=pbuf@entry=0x7fffffff9d90, max_len=max_len@entry=0, format=format@entry=0x1188930 "Failed to load magic database at '%s'.", 
    ap=ap@entry=0x7fffffff9e30) at /root/php-src/main/spprintf.c:821
#4  0x0000000000a88caf in php_verror (docref=0x0, params=params@entry=0x116a24a "", type=type@entry=2, 
    format=format@entry=0x1188930 "Failed to load magic database at '%s'.", args=args@entry=0x7fffffff9e30) at /root/php-src/main/main.c:786
#5  0x0000000000a8a644 in php_error_docref0 (docref=docref@entry=0x0, type=type@entry=2, format=format@entry=0x1188930 "Failed to load magic database at '%s'.")
    at /root/php-src/main/main.c:965
#6  0x00000000006e6338 in zif_finfo_open (ht=<optimized out>, return_value=0x18779b0, return_value_ptr=<optimized out>, this_ptr=0x0, return_value_used=<optimized out>)
    at /root/php-src/ext/fileinfo/fileinfo.c:348
#7  0x00000000010702a0 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /root/php-src/Zend/zend_vm_execute.h:558
#8  0x0000000000e40689 in execute_ex (execute_data=0x1844f10) at /root/php-src/Zend/zend_vm_execute.h:363
#9  0x0000000000d0409d 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
#10 0x0000000000a92d42 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd4a0) at /root/php-src/main/main.c:2610
#11 0x000000000107b1d1 in do_cli (argc=4, argv=0x17588a0) at /root/php-src/sapi/cli/php_cli.c:994
#12 0x00000000004212e9 in main (argc=4, argv=0x17588a0) at /root/php-src/sapi/cli/php_cli.c:1378
(gdb) x/i $rip
=> 0x7ffff736ff31 <_int_malloc+689>:    mov    %r14,0x10(%r9)
(gdb) i r
rax            0x7fffffff939f   140737488327583
rbx            0x7ffff76ae760   140737344366432
rcx            0x0      0
rdx            0x7ffff76ae788   140737344366472
rsi            0x7a0    1952
rdi            0x7ffff76ae760   140737344366432
rbp            0x60     0x60
rsp            0x7fffffff9310   0x7fffffff9310
r8             0x4      4
r9             0x0      0
r10            0x0      0
r11            0x416c20 4287520
r12            0x1878bb0        25660336
r13            0x6      6
r14            0x7ffff76ae7b8   140737344366520
r15            0x2710   10000
rip            0x7ffff736ff31   0x7ffff736ff31 <_int_malloc+689>
eflags         0x10287  [ CF PF SF IF RF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0


When compiled WITH ASAN, and USE_ZEND_ALLOC is unset (crash).
 $ ./php-5.6.18-asan magic-open.php magic.crash-asan /dev/null
ASAN:SIGSEGV
=================================================================
==20824== ERROR: AddressSanitizer: SEGV on unknown address 0x00000001f168 (pc 0x000000f9d7d4 sp 0x7ffc11db3770 bp 0x000000000000 T0)
AddressSanitizer can not provide additional info.
    #0 0xf9d7d3 in zend_mm_remove_from_free_list /root/php-src/Zend/zend_alloc.c:809
    #1 0xfa35f2 in _zend_mm_alloc_int /root/php-src/Zend/zend_alloc.c:2021
    #2 0xddffe7 in xbuf_format_converter /root/php-src/main/spprintf.c:794
    #3 0xde6b75 in vspprintf /root/php-src/main/spprintf.c:821
    #4 0xde6b75 in spprintf /root/php-src/main/spprintf.c:840
    #5 0xdc0d47 in php_verror /root/php-src/main/main.c:852
    #6 0xdc142a in php_error_docref0 /root/php-src/main/main.c:965
    #7 0x7fe8ca in zif_finfo_open /root/php-src/ext/fileinfo/fileinfo.c:348
    #8 0x17f7969 in zend_do_fcall_common_helper_SPEC /root/php-src/Zend/zend_vm_execute.h:558
    #9 0x139d03d in execute_ex /root/php-src/Zend/zend_vm_execute.h:363
    #10 0x11816fa in zend_execute_scripts /root/php-src/Zend/zend.c:1341
    #11 0xdcb6f1 in php_execute_script /root/php-src/main/main.c:2610
    #12 0x1806199 in do_cli /root/php-src/sapi/cli/php_cli.c:994
    #13 0x43622f in main /root/php-src/sapi/cli/php_cli.c:1378
    #14 0x7f87f6409ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #15 0x4373ac in _start (/root/php-src/php-5.6.18-asan+0x4373ac)
SUMMARY: AddressSanitizer: SEGV /root/php-src/Zend/zend_alloc.c:809 zend_mm_remove_from_free_list
==20824== ABORTING
Aborted


And compiled with ASAN and USE_ZEND_ALLOC set to 0:

 $ USE_ZEND_ALLOC=0 ./php-5.6.18-asan magic-open.php magic.crash-asan /dev/null                                                                                     
=================================================================
==20849== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60340000cd04 at pc 0x8664d0 bp 0x7ffcc02d84b0 sp 0x7ffcc02d84a8
WRITE of size 4 at 0x60340000cd04 thread T0
    #0 0x8664cf in file_check_mem /root/php-src/ext/fileinfo/libmagic/funcs.c:426
    #1 0x80cd7b in parse /root/php-src/ext/fileinfo/libmagic/apprentice.c:1520
    #2 0x80cd7b in load_1 /root/php-src/ext/fileinfo/libmagic/apprentice.c:1022
    #3 0x8184aa in apprentice_load /root/php-src/ext/fileinfo/libmagic/apprentice.c:1215
    #4 0x81c6dc in apprentice_1 /root/php-src/ext/fileinfo/libmagic/apprentice.c:417
    #5 0x823594 in file_apprentice /root/php-src/ext/fileinfo/libmagic/apprentice.c:603
    #6 0x7fe571 in zif_finfo_open /root/php-src/ext/fileinfo/fileinfo.c:347
    #7 0x17f7969 in zend_do_fcall_common_helper_SPEC /root/php-src/Zend/zend_vm_execute.h:558
    #8 0x139d03d in execute_ex /root/php-src/Zend/zend_vm_execute.h:363
    #9 0x11816fa in zend_execute_scripts /root/php-src/Zend/zend.c:1341
    #10 0xdcb6f1 in php_execute_script /root/php-src/main/main.c:2610
    #11 0x1806199 in do_cli /root/php-src/sapi/cli/php_cli.c:994
    #12 0x43622f in main /root/php-src/sapi/cli/php_cli.c:1378
    #13 0x7f773df01ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #14 0x4373ac in _start (/root/php-src/php-5.6.18-asan+0x4373ac)
0x60340000cd04 is located 36 bytes to the right of 480-byte region [0x60340000cb00,0x60340000cce0)
allocated by thread T0 here:
    #0 0x7f773e9df55f (/usr/lib/x86_64-linux-gnu/libasan.so.0+0x1555f)
    #1 0x8662ab in file_check_mem /root/php-src/ext/fileinfo/libmagic/funcs.c:418
SUMMARY: AddressSanitizer: heap-buffer-overflow /root/php-src/ext/fileinfo/libmagic/funcs.c:429 file_check_mem
Shadow bytes around the buggy address:
  0x0c06ffff9950: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c06ffff9960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff9970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff9980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff9990: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
=>0x0c06ffff99a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c06ffff99b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff99c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff99d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff99e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c06ffff99f0: 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
==20849== ABORTING
Aborted



Right, so those are all the possible crash states, the patch to fix is simple:

diff --git a/ext/fileinfo/libmagic/funcs.c b/ext/fileinfo/libmagic/funcs.c
index bd6d3d5..aefb95d 100644
--- a/ext/fileinfo/libmagic/funcs.c
+++ b/ext/fileinfo/libmagic/funcs.c
@@ -414,7 +414,7 @@ file_check_mem(struct magic_set *ms, unsigned int level)
        size_t len;
 
        if (level >= ms->c.len) {
-               len = (ms->c.len += 20) * sizeof(*ms->c.li);
+               while (level >= ms->c.len) len = (ms->c.len += 20) * sizeof(*ms->c.li);
                ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
                    emalloc(len) :
                    erealloc(ms->c.li, len));


Reasoning for that patch is with these tests, level is set to either 31 (noasan test file), or 32 (asan test file), and ms->c.len is 10. Originally it added 20 to the length, then realloc'd the memory chunk, then indexed into the memory at position "level". This overflowed the memory, and a write occurred. This patch ensures that the memory length is over the size of level.

You can see this from some gdb sessions:

$ gdb -ex 'break file_check_mem' -ex run -ex bt -ex 'p ms->c.len' -ex quit --args ./php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null 
<snip>
Breakpoint 1 at 0x7235a0: file /root/php-src/ext/fileinfo/libmagic/funcs.c, line 413.
Starting program: /root/php-src/php-5.6.18-noasan magic-open.php magic.crash-noasan /dev/null

Breakpoint 1, file_check_mem (ms=ms@entry=0x1879810, level=level@entry=31) at /root/php-src/ext/fileinfo/libmagic/funcs.c:413
413     {
#0  file_check_mem (ms=ms@entry=0x1879810, level=level@entry=31) at /root/php-src/ext/fileinfo/libmagic/funcs.c:413
#1  0x00000000006f1f3a in parse (action=<optimized out>, lineno=<optimized out>, line=0x7fffffff5bd0 '>' <repeats 31 times>, me=0x7fffffff5bc0, ms=<optimized out>)
    at /root/php-src/ext/fileinfo/libmagic/apprentice.c:1520
#2  load_1 (ms=ms@entry=0x1879810, action=action@entry=0, fn=fn@entry=0x18779e0 "/root/php-src/magic.crash-noasan", errs=errs@entry=0x7fffffff7c60, 
    mset=mset@entry=0x7fffffff7c70) at /root/php-src/ext/fileinfo/libmagic/apprentice.c:1022
#3  0x00000000006f9a03 in apprentice_load (ms=ms@entry=0x1879810, fn=fn@entry=0x18779e0 "/root/php-src/magic.crash-noasan", action=action@entry=0)
    at /root/php-src/ext/fileinfo/libmagic/apprentice.c:1215
#4  0x00000000006fdfa6 in apprentice_1 (ms=0x1879810, fn=0x18779e0 "/root/php-src/magic.crash-noasan", action=0) at /root/php-src/ext/fileinfo/libmagic/apprentice.c:417
#5  0x00000000006fffae in file_apprentice (ms=0x1879810, fn=0x18779e0 "/root/php-src/magic.crash-noasan", action=0)
    at /root/php-src/ext/fileinfo/libmagic/apprentice.c:603
#6  0x0000000000725bb7 in magic_load (ms=<optimized out>, magicfile=<optimized out>) at /root/php-src/ext/fileinfo/libmagic/magic.c:267
#7  0x00000000006e61e5 in zif_finfo_open (ht=<optimized out>, return_value=0x18779b0, return_value_ptr=<optimized out>, this_ptr=0x0, return_value_used=<optimized out>)
    at /root/php-src/ext/fileinfo/fileinfo.c:347
#8  0x00000000010702a0 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /root/php-src/Zend/zend_vm_execute.h:558
#9  0x0000000000e40689 in execute_ex (execute_data=0x1844f10) at /root/php-src/Zend/zend_vm_execute.h:363
#10 0x0000000000d0409d 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
#11 0x0000000000a92d42 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd4a0) at /root/php-src/main/main.c:2610
#12 0x000000000107b1d1 in do_cli (argc=4, argv=0x17588a0) at /root/php-src/sapi/cli/php_cli.c:994
#13 0x00000000004212e9 in main (argc=4, argv=0x17588a0) at /root/php-src/sapi/cli/php_cli.c:1378
$1 = 10


So looking at the possible attacks, without asan, and not using zend alloc, we have a segfault in alloc, where alloc is presumably trying to determine free space from unassigned memory that we wrote to when overflowing, this causes a crash. This is backed up when we look at the stack trace of when we run with asan, and using zend_alloc, where we get a segfault in zend_mm_remove_from_free_list, which is caused by a call to ZEND_MM_CHECK_TREE with a mm_block with an invalid parent pointer.

Running with asan, and not using zend alloc, pinpoints the location of the buffer overwrite, to be in file_check_mem, where we patched.

After the patch, there are no crashes, and you get the message the same as running without asan and with zend alloc.

Thanks for taking the time to read this report, sorry it was such a long one, just wanted to get across all the different scenarios.

Test script:
---------------
<?php
$finfo = finfo_open(FILEINFO_NONE, $argv[1]);
$info = finfo_file($finfo, $argv[2]);
var_dump($info);
?>

without ASAN and with USE_ZEND_ALLOC=0, use >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

with ASAN, with or without USE_ZEND_ALLOC=0, use >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>q>>>>>>>>



Expected result:
----------------
Warning: finfo_open(): Failed to load magic database at '/root/php-src/magic.crash-noasan'. in /root/php-src/magic-open.php on line 2

Warning: finfo_file() expects parameter 1 to be resource, boolean given in /root/php-src/magic-open.php on line 3
bool(false)


Actual result:
--------------
See description for each scenario's backtrace.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-02-14 03:08 UTC] hugh at allthethings dot co dot nz
Hey,

Just checking whether anyone has seen this?

Cheers,

Hugh
 [2016-02-15 08:29 UTC] stas@php.net
Could you please explain why you think it is a security issue? 
Also looks like your fix in libmagic, so I would advise submitting the bug upstream, since we do not maintain libmagic.
 [2016-02-15 21:08 UTC] hugh at allthethings dot co dot nz
Hey @stas,

I believe it is a security issue as I can reliably overflow the buffer and write, which is shown to lead to a double free (ish) issue later on in one of those scenarios. Happy for you to convince me why you think it isn't a security issue.

As for filing upstream, sure thing. Is that something you guys normally do, or should I go and do it?

Cheers,

Hugh
 [2016-02-15 21:23 UTC] stas@php.net
We usually prefer the original reporter did it, since you are the one who knows all the details, and also due the credit for the discovery of the issue. We would appreciate posting here the upstream ticket if one is created, so we could track it and sync our local copies if necessary.
 [2016-02-22 07:32 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2016-02-22 07:32 UTC] stas@php.net
DId you report it to the upstream?
 [2016-02-22 07:50 UTC] hugh at allthethings dot co dot nz
-Status: Feedback +Status: Open
 [2016-02-22 07:50 UTC] hugh at allthethings dot co dot nz
Hey Stas,

Not quite yet, I was at a conference over the weekend which is when I normally do my bug filing. I'll most likely be doing that this Friday, if not before.

Cheers,

Hugh
 [2016-02-23 04:04 UTC] hugh at allthethings dot co dot nz
Filed upstream at http://bugs.gw.com/view.php?id=522 (currently private). Let me know what emails you want attached to that bug, and when I get upstreams I guess they should be added to this?

Cheers,

Hugh
 [2016-02-23 19:35 UTC] hugh at allthethings dot co dot nz
Got a reply, looks like you just need to merge in the latest from your version of 5.22 to something more recent.



This has been fixed differently in:
1.82 (christos 03-Jun-15): len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);

Which is part of 5.23 and later.
 [2016-03-01 20:31 UTC] hugh at allthethings dot co dot nz
Hey @stas,

Just checking whether you saw my update. You just need to update to latest libmagic version, or just to 5.23. If you like I can get a patch for that?

Cheers,

Hugh
 [2016-03-02 07:14 UTC] stas@php.net
Yes, I saw it but didn't have time yet to address it. If you have a patch for this, please add it (you can create secret gist on gist.github.com and post the link here).
 [2016-04-01 11:42 UTC] kaplan@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: kaplan
 [2016-04-01 11:42 UTC] kaplan@php.net
Bug fixed in 5.5.34, 5.6.20 and 7.0.5.
 [2016-04-25 17:08 UTC] remi@php.net
-CVE-ID: +CVE-ID: 2015-8865
 [2016-07-20 11:32 UTC] davey@php.net
Automatic comment on behalf of ab
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e93c6910fceb62d19fe143e351a86eee8df9b456
Log: Fixed bug #71527 Buffer over-write in finfo_open with malformed magic file
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC