php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72742 memory allocator fails to realloc small block to large one
Submitted: 2016-08-03 06:20 UTC Modified: 2016-09-05 15:29 UTC
From: tinduong at vnsecurity dot net Assigned: stas (profile)
Status: Closed Package: Filesystem function related
PHP Version: 7.0.9 OS: *
Private report: No CVE-ID: 2016-7133
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: tinduong at vnsecurity dot net
New email:
PHP Version: OS:

 

 [2016-08-03 06:20 UTC] tinduong at vnsecurity dot net
Description:
------------
PHPAPI php_check_specific_open_basedir in php-src/main/fopen_wrappers.c has a integer overflow vulnerability leads to buffer overflow if open_basedir is set.

<snippet php-src/main/fopen_wrappers.c:138>
```
PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path)
{
	char resolved_name[MAXPATHLEN];
<...>
	path_len = (int)strlen(path);
	if (path_len > (MAXPATHLEN - 1)) { /*path_len can be overflow to negative value and pass this check */
		/* empty and too long paths are invalid */
		return -1;
	}
	/* normalize and expand path */
	if (expand_filepath(path, resolved_name) == NULL) {
		return -1;
	}

	path_len = (int)strlen(resolved_name);
	memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
```
</snippet>

PoC here using fopen, but it is not the only way since many functions are affected by open_basedir

I propose the following patch:

--- a/main/fopen_wrappers.c
+++ b/main/fopen_wrappers.c
@@ -154,7 +154,7 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path
        }

        path_len = (int)strlen(path);
-       if (path_len > (MAXPATHLEN - 1)) {
+       if (path_len <= 0 || path_len > (MAXPATHLEN - 1)) {
                /* empty and too long paths are invalid */
                return -1;
        }



Test script:
---------------
<?php
	ini_set('memory_limit', -1);
	ini_set('open_basedir', '/');
	fopen(str_repeat('a', 0xfffffff0), 'r');
?>

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

Actual result:
--------------
Stopped reason: SIGSEGV
#0  __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:37
#1  0x00000000007bf277 in xbuf_format_converter (xbuf=0x7fffffffac10, is_char=0x1, fmt=0xd70849 "s", ap=0x7fffffffad58)
    at /home/vps/git/php-src/main/spprintf.c:814
#2  0x00000000007bf60d in vspprintf (pbuf=0x7fffffffac90, max_len=0x0,
    format=0xd707f8 "File name is longer than the maximum allowed path length on this platform (%d): %s", ap=0x7fffffffad58)
    at /home/vps/git/php-src/main/spprintf.c:843
#3  0x00000000007b4f7b in php_verror (docref=0x0, params=0xd6eff1 "", type=0x2,
    format=0xd707f8 "File name is longer than the maximum allowed path length on this platform (%d): %s", args=0x7fffffffad58)
    at /home/vps/git/php-src/main/main.c:756
#4  0x00000000007b59d8 in php_error_docref0 (docref=0x0, type=0x2,
    format=0xd707f8 "File name is longer than the maximum allowed path length on this platform (%d): %s")
    at /home/vps/git/php-src/main/main.c:943
#5  0x00000000007c0144 in php_check_open_basedir_ex (path=0x7ffef5a00018 'a' <repeats 200 times>..., warn=0x1)
    at /home/vps/git/php-src/main/fopen_wrappers.c:295
#6  0x00000000007c00ce in php_check_open_basedir (path=0x7ffef5a00018 'a' <repeats 200 times>...)
    at /home/vps/git/php-src/main/fopen_wrappers.c:279
#7  0x00000000007e12b9 in php_plain_files_stream_opener (wrapper=0x10a7980 <php_plain_files_wrapper>,
    path=0x7ffef5a00018 'a' <repeats 200 times>..., mode=0x7ffff6858e58 "r", options=0x0, opened_path=0x0, context=0x7ffff6858d40,
    php_stream_call_depth=0x1, zend_filename=0xd723f8 "/home/vps/git/php-src/main/streams/streams.c", __zend_lineno=0x809,
    zend_orig_filename=0xd550d0 "/home/vps/git/php-src/ext/standard/file.c", zend_orig_lineno=0x366)
    at /home/vps/git/php-src/main/streams/plain_wrapper.c:1058
#8  0x00000000007da8db in _php_stream_open_wrapper_ex (path=0x7ffef5a00018 'a' <repeats 200 times>..., mode=0x7ffff6858e58 "r", options=0x8,
    opened_path=0x0, context=0x7ffff6858d40, __php_stream_call_depth=0x0,
    zend_filename=0xd550d0 "/home/vps/git/php-src/ext/standard/file.c", zend_lineno=0x366, __zend_orig_filename=0x0,
    __zend_orig_lineno=0x0) at /home/vps/git/php-src/main/streams/streams.c:2055
#9  0x0000000000730e32 in php_if_fopen (execute_data=0x7ffff6814100, return_value=0x7ffff68140e0)
    at /home/vps/git/php-src/ext/standard/file.c:870
#10 0x0000000000670167 in phar_fopen (execute_data=0x7ffff6814100, return_value=0x7ffff68140e0)
    at /home/vps/git/php-src/ext/phar/func_interceptors.c:427
#11 0x00000000008ae50b in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER () at /home/vps/git/php-src/Zend/zend_vm_execute.h:675
#12 0x00000000008adc2e in execute_ex (ex=0x7ffff6814030) at /home/vps/git/php-src/Zend/zend_vm_execute.h:429
#13 0x00000000008add40 in zend_execute (op_array=0x7ffff687c000, return_value=0x0) at /home/vps/git/php-src/Zend/zend_vm_execute.h:474
#14 0x000000000084e490 in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3) at /home/vps/git/php-src/Zend/zend.c:1447
#15 0x00000000007b9126 in php_execute_script (primary_file=0x7fffffffd6c0) at /home/vps/git/php-src/main/main.c:2533
#16 0x000000000092d910 in do_cli (argc=0x2, argv=0x10d5770) at /home/vps/git/php-src/sapi/cli/php_cli.c:990
#17 0x000000000092ead4 in main (argc=0x2, argv=0x10d5770) at /home/vps/git/php-src/sapi/cli/php_cli.c:1378
#18 0x00007ffff6faaf45 in __libc_start_main (main=0x92e2cc <main>, argc=0x2, argv=0x7fffffffea68, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffea58) at libc-start.c:287
#19 0x0000000000422ca9 in _start ()

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-03 06:44 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-08-03 06:44 UTC] stas@php.net
This sounds rather artificial to be security issue.
 [2016-08-03 06:55 UTC] tinduong at vnsecurity dot net
There is CVE-2010-3436 releated to open_basedir, so I think this is a security issue as well. Please review it.
 [2016-08-03 07:24 UTC] tinduong at vnsecurity dot net
-Type: Bug +Type: Security -Private report: No +Private report: Yes
 [2016-08-03 07:24 UTC] tinduong at vnsecurity dot net
There is CVE-2010-3436 releated to open_basedir, so I think this is a security issue as well. Please review it
 [2016-08-03 07:33 UTC] stas@php.net
I'm not sure how existance of CVE-2010-3436 changes anything.
 [2016-08-03 07:58 UTC] tinduong at vnsecurity dot net
As I mentioned before, fopen is not the only way. If an user has ability to use any function that open/read/write a file on server with an arbitraty length, this vulnerability can leads to DoS or RCE in some situations. include, file_get_contents are also vulnerable for instance.
 [2016-08-11 05:05 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2016-08-11 05:05 UTC] stas@php.net
In your example, the error is not in the code you were talking about. It actually would never get there, as before that it'd get to php_check_open_basedir_ex which checks the length. Your backtrace shows crash in the error message printing.

The crash seems to be happening because of some problem in allocation routines, I'll dig deeper to see what's going on.
 [2016-08-11 05:19 UTC] stas@php.net
-Status: Feedback +Status: Open
 [2016-08-11 07:08 UTC] tinduong at vnsecurity dot net
You are correct ! The error is not occurred in php_check_specific_open_basedir. Sorry for the mistake.
The error in fact occurred in xbuf_format_converter (which is called by vspprintf() in php_verror() ) while trying to print error message contains my large input. 
In the below debugging result, it seems like memcpy() tries to read an unmapped memory location, which causes segment fault.
Although the root cause is not what I mentioned in the previous comments, I do think this a security issue.

Debugging result:
-------------------
Continuing.
[----------------------------------registers-----------------------------------]
RAX: 0x7ffef6600018 ('a' <repeats 200 times>...)
RBX: 0x1
RCX: 0x7ffff687f252 --> 0x5060000000000000 ('')
RDX: 0xfffffff0
RSI: 0x7ffef6600018 ('a' <repeats 200 times>...)
RDI: 0x7ffff687f252 --> 0x5060000000000000 ('')
RBP: 0x7fffffffa800 --> 0x7fffffffa850 --> 0x7fffffffa940 --> 0x7fffffffaa40 --> 0x7fffffffaa80 --> 0x7fffffffaaa0 (--> ...)
RSP: 0x7fffffffa2c0 --> 0x0
RIP: 0x7b6545 (<xbuf_format_converter+10062>:	call   0x422a80 <memcpy@plt>)
R8 : 0xd65060 ("/home/ubuntu/php-src/main/spprintf.c")
R9 : 0x32e
R10: 0x7d8555 (<php_plain_files_stream_opener>:	push   rbp)
R11: 0x7ffff6858898 --> 0x72 ('r')
R12: 0x422c80 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe680 --> 0x2
R14: 0x7ffff6814030 --> 0x7ffff68691e0 --> 0x8a5593 (<ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER>:	push   rbp)
R15: 0x7ffff68691e0 --> 0x8a5593 (<ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER>:	push   rbp)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7b6538 <xbuf_format_converter+10049>:	mov    rax,QWORD PTR [rbp-0x4b0]
   0x7b653f <xbuf_format_converter+10056>:	mov    rsi,rax
   0x7b6542 <xbuf_format_converter+10059>:	mov    rdi,rcx
=> 0x7b6545 <xbuf_format_converter+10062>:	call   0x422a80 <memcpy@plt>
   0x7b654a <xbuf_format_converter+10067>:	mov    rax,QWORD PTR [rbp-0x460]
   0x7b6551 <xbuf_format_converter+10074>:	mov    rdx,QWORD PTR [rbp-0x470]
   0x7b6558 <xbuf_format_converter+10081>:	mov    QWORD PTR [rax+0x8],rdx
   0x7b655c <xbuf_format_converter+10085>:	jmp    0x7b6580 <xbuf_format_converter+10121>
Guessed arguments:
arg[0]: 0x7ffff687f252 --> 0x5060000000000000 ('')
arg[1]: 0x7ffef6600018 ('a' <repeats 200 times>...)
arg[2]: 0xfffffff0
arg[3]: 0x7ffff687f252 --> 0x5060000000000000 ('')
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa2c0 --> 0x0
0008| 0x7fffffffa2c8 --> 0x0
0016| 0x7fffffffa2d0 --> 0x0
0024| 0x7fffffffa2d8 --> 0x0
0032| 0x7fffffffa2e0 --> 0x7fffffffa978 --> 0x3000000028 ('(')
0040| 0x7fffffffa2e8 --> 0xd65501 --> 0x6f00000000000073 ('s')
0048| 0x7fffffffa2f0 --> 0x100000000
0056| 0x7fffffffa2f8 --> 0x7fffffffa830 --> 0x7ffff687f200 ("File name is longer than the maximum allowed path length on this platform (4096): ")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x00000000007b6545 in xbuf_format_converter (xbuf=0x7fffffffa830, is_char=0x1, fmt=0xd65501 "s", ap=0x7fffffffa978)
    at /home/ubuntu/php-src/main/spprintf.c:814
814				INS_STRING(xbuf, s, s_len, is_char);
gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0xfffffffdffd80dd6
RBX: 0x1
RCX: 0x1ffffffe0
RDX: 0xfffffff0
RSI: 0x7ffef6600018 ('a' <repeats 200 times>...)
RDI: 0x7ffff687f252 ('a' <repeats 16 times>)
RBP: 0x7fffffffa800 --> 0x7fffffffa850 --> 0x7fffffffa940 --> 0x7fffffffaa40 --> 0x7fffffffaa80 --> 0x7fffffffaaa0 (--> ...)
RSP: 0x7fffffffa2b8 --> 0x7b654a (<xbuf_format_converter+10067>:	mov    rax,QWORD PTR [rbp-0x460])
RIP: 0x7ffff7020ef5 (movdqu XMMWORD PTR [rdi+rdx*1-0x10],xmm8)
R8 : 0xd65060 ("/home/ubuntu/php-src/main/spprintf.c")
R9 : 0x32e
R10: 0x7d8555 (<php_plain_files_stream_opener>:	push   rbp)
R11: 0x7ffff6858898 --> 0x72 ('r')
R12: 0x422c80 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe680 --> 0x2
R14: 0x7ffff6814030 --> 0x7ffff68691e0 --> 0x8a5593 (<ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER>:	push   rbp)
R15: 0x7ffff68691e0 --> 0x8a5593 (<ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER>:	push   rbp)
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7020ee5:	cmp    rdx,0x20
   0x7ffff7020ee9:	movdqu XMMWORD PTR [rdi],xmm8
   0x7ffff7020eee:	movdqu xmm8,XMMWORD PTR [rsi+rdx*1-0x10]
=> 0x7ffff7020ef5:	movdqu XMMWORD PTR [rdi+rdx*1-0x10],xmm8
   0x7ffff7020efc:	ja     0x7ffff7020f10
   0x7ffff7020efe:	mov    rax,rdi
   0x7ffff7020f01:	ret
   0x7ffff7020f02:	data16 data16 data16 data16 nop WORD PTR cs:[rax+rax*1+0x0]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa2b8 --> 0x7b654a (<xbuf_format_converter+10067>:	mov    rax,QWORD PTR [rbp-0x460])
0008| 0x7fffffffa2c0 --> 0x0
0016| 0x7fffffffa2c8 --> 0x0
0024| 0x7fffffffa2d0 --> 0x0
0032| 0x7fffffffa2d8 --> 0x0
0040| 0x7fffffffa2e0 --> 0x7fffffffa978 --> 0x3000000028 ('(')
0048| 0x7fffffffa2e8 --> 0xd65501 --> 0x6f00000000000073 ('s')
0056| 0x7fffffffa2f0 --> 0x100000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7020ef5 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
 [2016-08-15 02:15 UTC] stas@php.net
-Operating System: +Operating System: * -PHP Version: 7.1Git-2016-08-03 (Git) +PHP Version: 7.0.9 -Assigned To: +Assigned To: stas
 [2016-08-15 02:15 UTC] stas@php.net
The problem is that memory allocator fails to reallocate block properly if old block was small but new block is huge. 

The fix is in security repo as c2a13ced4272f2e65d2773e2ea6ca11c1ce4a911 and in https://gist.github.com/97abe94b25f99e6426e37b8693f46063 (patch by nikic)

please verify
 [2016-08-15 06:02 UTC] stas@php.net
-CVE-ID: +CVE-ID: needed
 [2016-08-15 07:23 UTC] tinduong at vnsecurity dot net
It seems to be fixed. I can't reproduce it anymore. 

May I ask for a CVE number ?

Thanks for your time.
 [2016-08-17 08:23 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c2a13ced4272f2e65d2773e2ea6ca11c1ce4a911
Log: Fix bug #72742 - memory allocator fails to realloc small block to large one
 [2016-08-17 08:23 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-08-17 08:57 UTC] tinduong at vnsecurity dot net
-Summary: Stack-based buffer overflow vulnerability in php_check_specific_open_basedir +Summary: memory allocator fails to realloc small block to large one
 [2016-08-17 08:57 UTC] tinduong at vnsecurity dot net
correct bug name
 [2016-08-17 09:15 UTC] laruence@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c2a13ced4272f2e65d2773e2ea6ca11c1ce4a911
Log: Fix bug #72742 - memory allocator fails to realloc small block to large one
 [2016-08-17 12:04 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=326ac100ac016cd5fc2c7367c97d64fa4b6932df
Log: Fix bug #72742 - memory allocator fails to realloc small block to large one
 [2016-09-05 15:29 UTC] remi@php.net
-CVE-ID: needed +CVE-ID: 2016-7133
 [2016-10-17 10:09 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c2a13ced4272f2e65d2773e2ea6ca11c1ce4a911
Log: Fix bug #72742 - memory allocator fails to realloc small block to large one
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 05 07:01:30 2024 UTC