|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2016-06-29 06:09 UTC] loianhtuan at gmail dot com
Description:
------------
CWD_API virtual_file_ex in php-src/Zend/zend_virtual_cwd.c has a bug that allow memcpy a large chunk of memory leads to buffer overflow.
The root cause of this vulnerability is integer overflow of path_length variable.
<snippet php-src/Zend/zend_virtual_cwd.c:1243>
```
CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
{
int path_length = (int)strlen(path);
char resolved_path[MAXPATHLEN];
<...>
if (path_length == 0 || path_length >= MAXPATHLEN-1) { /*path_length can be overflow to negative value and passed this check*/
#ifdef ZEND_WIN32
_set_errno(EINVAL);
#else
errno = EINVAL;
#endif
return 1;
}
<...>
memcpy(resolved_path, path, path_length + 1);
```
</snippet>
PoC here is using ZipArchive to demonstrate the bug, but it may not be the only way.
I propose the following patch:
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@ -1251,7 +1251,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
int add_slash;
void *tmp;
- if (path_length == 0 || path_length >= MAXPATHLEN-1) {
+ if (path_length <= 0 || path_length >= MAXPATHLEN-1) {
#ifdef ZEND_WIN32
_set_errno(EINVAL);
#else
Thanks!
Test script:
---------------
<?php
ini_set('memory_limit',-1);
$a = new ZipArchive();
if ($a->open('a.zip', ZIPArchive::CREATE) !== TRUE){die("k9");};
$a->extractTo(".", str_repeat("/",0xfffffff0));
$a->close();
?>
Expected result:
----------------
No crash
Actual result:
--------------
Stopped reason: SIGSEGV
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:152
152 ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
gdb-peda$ bt
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:152
#1 0x0000000000885354 in virtual_file_ex (state=0x7fffffff7ee0, path=0x7ffef5a00018 '/' <repeats 200 times>...,
verify_path=0x0, use_realpath=0x0) at /home/vps/git/php-src/Zend/zend_virtual_cwd.c:1326
#2 0x00007ffff5abc416 in php_zip_extract_file (za=0x11d5920, dest=0x7ffff6858e98 ".",
file=0x7ffef5a00018 '/' <repeats 200 times>..., file_len=0xfffffff0) at /home/vps/git/php-src/ext/zip/php_zip.c:160
#3 0x00007ffff5ac28ae in c_ziparchive_extractTo (execute_data=0x7ffff6814120, return_value=0x7fffffffb110)
at /home/vps/git/php-src/ext/zip/php_zip.c:2639
#4 0x00000000008a6a09 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /home/vps/git/php-src/Zend/zend_vm_execute.h:973
#5 0x00000000008a53e9 in execute_ex (ex=0x7ffff6814030) at /home/vps/git/php-src/Zend/zend_vm_execute.h:428
#6 0x00000000008a54fb in zend_execute (op_array=0x7ffff687d000, return_value=0x0)
at /home/vps/git/php-src/Zend/zend_vm_execute.h:473
#7 0x00000000008465ee in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3)
at /home/vps/git/php-src/Zend/zend.c:1441
#8 0x00000000007b16a1 in php_execute_script (primary_file=0x7fffffffd6c0) at /home/vps/git/php-src/main/main.c:2515
#9 0x0000000000922222 in do_cli (argc=0x2, argv=0x10c3770) at /home/vps/git/php-src/sapi/cli/php_cli.c:993
#10 0x00000000009233e6 in main (argc=0x2, argv=0x10c3770) at /home/vps/git/php-src/sapi/cli/php_cli.c:1381
#11 0x00007ffff6faaf45 in __libc_start_main (main=0x922bde <main>, argc=0x2, argv=0x7fffffffea68, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffea58) at libc-start.c:287
#12 0x0000000000422ce9 in _start ()
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Nov 02 10:00:02 2025 UTC |
Thank for you prompt feedback! :) There is a code block later that use negative value so I'm not sure if we should change it to size_t. <snippet php-src/Zend/zend_virtual_cwd.c:1380> path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL); if (path_length < 0) { errno = ENOENT; return 1; } </snippet>