string length overflow in mb_encode_* function
| Bug #73082 | string length overflow in mb_encode_* function | ||||
|---|---|---|---|---|---|
| Submitted: | 2016-09-14 07:48 UTC | Modified: | 2017-02-13 01:19 UTC | ||
| From: | secresearch at fortinet dot com | Assigned: | stas (profile) | ||
| Status: | Closed | Package: | mbstring related | ||
| PHP Version: | 5.6.26RC1 | OS: | ALL | ||
| Private report: | No | CVE-ID: | None | ||
[2016-09-14 07:48 UTC] secresearch at fortinet dot com
Description:
------------
Multi byte encode function did not check the length of encode data, that cause output length will be overflow the integer and cause heap corruption:
mbfl_string * mbfl_html_numeric_entity(
mbfl_string *string,
mbfl_string *result,
int *convmap,
int mapsize,
int type)
{
...
/* feed data */
p = string->val;
n = string->len;
if (p != NULL) {
while (n > 0) { //this loop will cause the corruption
if ((*encoder->filter_function)(*p++, encoder) < 0) {
break;
}
n--;
}
}
mbfl_memory_device_output(int c, void *data)
{
mbfl_memory_device *device = (mbfl_memory_device *)data;
if (device->pos >= device->length) {
/* reallocate buffer */
int newlen;
unsigned char *tmp;
newlen = device->length + device->allocsz;
tmp = (unsigned char *)mbfl_realloc((void *)device->buffer, newlen*sizeof(unsigned char));
if (tmp == NULL) {
return -1;
}
device->length = newlen;
device->buffer = tmp;
}
device->buffer[device->pos++] = (unsigned char)c; //device->pos = -2147483647 -> crash
return c;
}
Patch:
------------
*check device->pos value in mbfl_memory_device_output
*or just check the output length must accepted (<int_max) in function mbfl_html_numeric_entity
Test script:
---------------
<?php
ini_set('memory_limit', -1);
$str = str_repeat("a", 0xffffffff/9); //a -> a output_len = input_len*5 -> overflow integer
var_dump(strlen($str));
$str1 = mb_encode_numericentity ($str, array (0x0, 0xffff, 0, 0xffff), 'UTF-8');
?>
Expected result:
----------------
No Crash
Actual result:
--------------
Starting program: /home/test/PHP-5.6.26/sapi/cli/php ~/phptestcase/testmb_encode_numericentity_negative.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
int(2147483646)
Program received signal SIGSEGV, Segmentation fault.
0x000000000070a97a in mbfl_memory_device_output (c=38, data=0x7fffffffa670) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/mbfl/mbfl_memory_device.c:157
157 device->buffer[device->pos++] = (unsigned char)c;
(gdb) bt
#0 0x000000000070a97a in mbfl_memory_device_output (c=38, data=0x7fffffffa670) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/mbfl/mbfl_memory_device.c:157
#1 0x0000000000701f74 in mbfl_filt_conv_wchar_utf8 (c=38, filter=0x7ffff7fa12c8) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/filters/mbfilter_utf8.c:219
#2 0x00000000007083c6 in collector_encode_htmlnumericentity (c=26085, data=0x7fffffffa690) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/mbfl/mbfilter.c:2700
#3 0x0000000000701cf6 in mbfl_filt_conv_utf8_wchar (c=165, filter=0x7ffff7fa1348) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/filters/mbfilter_utf8.c:139
#4 0x00000000007093c8 in mbfl_html_numeric_entity (string=0x7fffffffa730, result=0x7fffffffa710, convmap=0x7ffff7fa1b48, mapsize=1, type=0) at /home/test/PHP-5.6.26/ext/mbstring/libmbfl/mbfl/mbfilter.c:3092
#5 0x0000000000714548 in php_mb_numericentity_exec (ht=3, return_value=0x7ffff7fa1298, return_value_ptr=0x7ffff7f6c150, this_ptr=0x0, return_value_used=1, type=0) at /home/test/PHP-5.6.26/ext/mbstring/mbstring.c:3804
#6 0x00000000007145ff in zif_mb_encode_numericentity (ht=3, return_value=0x7ffff7fa1298, return_value_ptr=0x7ffff7f6c150, this_ptr=0x0, return_value_used=1) at /home/test/PHP-5.6.26/ext/mbstring/mbstring.c:3818
#7 0x00000000009ccf9e in zend_do_fcall_common_helper_SPEC (execute_data=0x7ffff7f6c248) at /home/test/PHP-5.6.26/Zend/zend_vm_execute.h:558
#8 0x00000000009d4bce in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x7ffff7f6c248) at /home/test/PHP-5.6.26/Zend/zend_vm_execute.h:2602
#9 0x00000000009cb491 in execute_ex (execute_data=0x7ffff7f6c248) at /home/test/PHP-5.6.26/Zend/zend_vm_execute.h:363
#10 0x00000000009cbe7d in zend_execute (op_array=0x7ffff7fa09e0) at /home/test/PHP-5.6.26/Zend/zend_vm_execute.h:388
#11 0x00000000009873bc in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/test/PHP-5.6.26/Zend/zend.c:1341
#12 0x00000000008f826e in php_execute_script (primary_file=0x7fffffffe280) at /home/test/PHP-5.6.26/main/main.c:2613
#13 0x0000000000aaa1b2 in do_cli (argc=2, argv=0x1382960) at /home/test/PHP-5.6.26/sapi/cli/php_cli.c:994
#14 0x0000000000aab200 in main (argc=2, argv=0x1382960) at /home/test/PHP-5.6.26/sapi/cli/php_cli.c:1378
(gdb)
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2016-09-25 23:08 UTC] stas@php.net
-Summary: memory corruption in mb_encode_* function +Summary: string length overflow in mb_encode_* function -Assigned To: +Assigned To: stas
[2016-09-26 04:10 UTC] secresearch at fortinet dot com
[2016-09-26 06:34 UTC] secresearch at fortinet dot com
The patch must looks like this: --- PHP-5.6.26_old/ext/mbstring/libmbfl/mbfl/mbfilter.c 2016-09-26 11:54:21.369998637 +0800 +++ PHP-5.6.26/ext/mbstring/libmbfl/mbfl/mbfilter.c 2016-09-26 14:32:27.536978132 +0800 @@ -111,6 +111,7 @@ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46 }; +#define INT_MAX_LIMIT ((1UL << (SIZEOF_INT * 8 - 1)) - 1) /* @@ -3042,7 +3043,7 @@ int n; unsigned char *p; - if (string == NULL || result == NULL) { + if (string == NULL || result == NULL || (string->len > INT_MAX_LIMIT/5)) { return NULL; } mbfl_string_init(result); output_len <= input_len*5 (5 times is the maximum output length, this depend on convmap, type of encode. So I just leave 5 here for quick fix)[2016-10-11 23:45 UTC] stas@php.net
-Status: Assigned +Status: Closed
[2017-02-13 01:19 UTC] stas@php.net
-Type: Security +Type: Bug