kyz/libmspack

Heap buffer overflow in chmd_read_headers()

JsHuang opened this issue · 7 comments

Description:

Function chmd_read_headers() in libmspack has a heap buffer overflow problem( Out of Bound Read).

Affected version:

libmspack 0.9.1 alpha

Details:

In function chmd_read_headers(), line 486,memcmp(&name[33], &content_name[33], 8L) will lead to out of bound read while extracting a crafted chm file.

chmd.c ,line 486~492:

      if (name[0] == ':' && name[1] == ':') {
        /* system file */
        if (memcmp(&name[2], &content_name[2], 31L) == 0) {
          if (memcmp(&name[33], &content_name[33], 8L) == 0) {
            chm->sec1.content = fi;
          }
          else if (memcmp(&name[33], &control_name[33], 11L) == 0) {
            chm->sec1.control = fi;
          }

Details with asan output:

./chmextract chmextract-overflow-chmd-486                      
chmextract-overflow-chmd-486
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '71'.
chmextract-overflow-chmd-486: invalid section number '266694953'.
chmextract-overflow-chmd-486: invalid section number '114'.
chmextract-overflow-chmd-486: invalid section number '84'.
chmextract-overflow-chmd-486: invalid section number '16259'.
chmextract-overflow-chmd-486: invalid section number '58'.
chmextract-overflow-chmd-486: invalid section number '67'.
chmextract-overflow-chmd-486: invalid section number '47'.
chmextract-overflow-chmd-486: invalid section number '48'.
chmextract-overflow-chmd-486: invalid section number '71'.
chmextract-overflow-chmd-486: invalid section number '266694953'.
chmextract-overflow-chmd-486: invalid section number '114'.
chmextract-overflow-chmd-486: invalid section number '84'.
chmextract-overflow-chmd-486: invalid section number '16259'.
chmextract-overflow-chmd-486: invalid section number '58'.
=================================================================
==9457==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000002500 at pc 0x0000004bf5bc bp 0x7ffebbdd6c60 sp 0x7ffebbdd6410
READ of size 31 at 0x621000002500 thread T0
    #0 0x4bf5bb in __interceptor_memcmp.part.78 /src/llvm/projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:827
    #1 0x7f1b498ccde1 in chmd_read_headers /src/libmspack/libmspack/mspack/chmd.c:486
    #2 0x7f1b498ccde1 in chmd_real_open /src/libmspack/libmspack/mspack/chmd.c:163
    #3 0x7f1b498ccde1 in chmd_open /src/libmspack/libmspack/mspack/chmd.c:126
    #4 0x529a23 in main /src/libmspack/libmspack/examples/chmextract.c:92:18
    #5 0x7f1b489c882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #6 0x41a888 in _start (/src/libmspack/libmspack/examples/.libs/chmextract+0x41a888)

0x621000002500 is located 0 bytes to the right of 4096-byte region [0x621000001500,0x621000002500)
allocated by thread T0 here:
    #0 0x4e95bf in __interceptor_malloc /src/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:146
    #1 0x7f1b498cbd57 in chmd_read_headers /src/libmspack/libmspack/mspack/chmd.c:418
    #2 0x7f1b498cbd57 in chmd_real_open /src/libmspack/libmspack/mspack/chmd.c:163
    #3 0x7f1b498cbd57 in chmd_open /src/libmspack/libmspack/mspack/chmd.c:126

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/llvm/projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcmp.part.78
Shadow bytes around the buggy address:
  0x0c427fff8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c427fff84a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84f0: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==9457==ABORTING

poc file

https://github.com/JsHuang/pocs/blob/master/libmspack/chmextract-overflow-chmd-486

Credit: ADLab of Venustech

kyz commented

Thank you for reporting this, and the POC file.

The issue is that checking for specific system file names does not take name length into consideration. The memcmp() will read past the end of the freshly-allocated name buffer. It's not especially exploitable (overread... past a freshly allocated buffer, not attacker controlled), but nonetheless, it is a memory error and has been fixed in commit 2f08413.

This issue apparently got assigned the CVE-2019-1010305 CVE id.

kyz commented

Thanks for letting me know. I've added the CVE id to the existing entry on the list of libmspack security vulnerabilities

I have run this POC but this was my output.
why?

chmextract-overflow-chmd-486
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '89'.
chmextract-overflow-chmd-486: invalid section number '71'.
chmextract-overflow-chmd-486: invalid section number '266694953'.
chmextract-overflow-chmd-486: invalid section number '114'.
chmextract-overflow-chmd-486: invalid section number '84'.
chmextract-overflow-chmd-486: invalid section number '16259'.
chmextract-overflow-chmd-486: invalid section number '58'.
chmextract-overflow-chmd-486: invalid section number '67'.
chmextract-overflow-chmd-486: invalid section number '47'.
chmextract-overflow-chmd-486: invalid section number '48'.
chmextract-overflow-chmd-486: invalid section number '71'.
chmextract-overflow-chmd-486: invalid section number '266694953'.
chmextract-overflow-chmd-486: invalid section number '114'.
chmextract-overflow-chmd-486: invalid section number '84'.
chmextract-overflow-chmd-486: invalid section number '16259'.
chmextract-overflow-chmd-486: invalid section number '58'.
chmextract-overflow-chmd-486: WARNING; contents are corrupt
Extracting ▒▒▒
Extracting  index.ht
Extracting YYYYYYYYYYYYYYYITSF
Extracting $FIftiMain
Extracting Documents/Table o& Contents.hhc
chmextract-overflow-chmd-486: extract error on "/Documents/Table o& Contents.hhc": error in data format
Extracting $FIftiMain
Extracting Documents/Table of Contents.hhc
chmextract-overflow-chmd-486: extract error on "/Documents/Table of Contents.hhc": error in data format
Extracting $FIftiMain
Extracting ▒▒▒
Extracting $FIftiMain
Extracting Documents/Table of Contents.hhc
chmextract-overflow-chmd-486: extract error on "/Documents/Table of Contents.hhc": error in data format
Extracting $FIftiMain
Extracting Documents/Table of Contents.hhc
chmextract-overflow-chmd-486: extract error on "/Documents/Table of Contents.hhc": error in data format
Extracting #IDXHDR▒L▒
chmextract-overflow-chmd-486: extract error on "#IDXHDR▒L▒": read error
Extracting #IDXHDR▒
chmextract-overflow-chmd-486: extract error on "#IDXHDR▒": read error
Extracting #IDXHDR▒
chmextract-overflow-chmd-486: extract error on "#IDXHDR▒": read error
Extracting #IDXHDR▒
chmextract-overflow-chmd-486: extract error on "#IDXHDR▒": read error
Extracting Doculents/Index.hhk
chmextract-overflow-chmd-486: extract error on "/Doculents/Index.hhk": error in data format
Extracting Documents/Index.hhk
chmextract-overflow-chmd-486: extract error on "/Documents/Index.hhk": error in data format
Extracting Doculents/Index.hhk
chmextract-overflow-chmd-486: extract error on "/Doculents/Index.hhk": error in data format
Extracting Documents/Index.hhk
chmextract-overflow-chmd-486: extract error on "/Documents/Index.hhk": error in data format
Extracting Documents/test1.html
chmextract-overflow-chmd-486: extract error on "/Documents/test1.html": error in data format
Extracting Documents/test1.html
chmextract-overflow-chmd-486: extract error on "/Documents/test1.html": error in data format
Extracting Documents/test1.html
chmextract-overflow-chmd-486: extract error on "/Documents/test1.html": error in data format
Extracting Documents/test1.html
chmextract-overflow-chmd-486: extract error on "/Documents/test1.html": error in data format
Extracting $WWKeywordLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWKeywordLinks/Property": error in data format
Extracting $WWKeywordLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWKeywordLinks/Property": error in data format
Extracting $WWKeywordLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWKeywordLinks/Property": error in data format
Extracting $WWKeywordLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWKeywordLinks/Property": error in data format
Extracting $WWAssociativeLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWAssociativeLinks/Property": error in data format
Extracting $WWAssociativeLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWAssociativeLinks/Property": error in data format
Extracting $WWAssociativeLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWAssociativeLinks/Property": error in data format
Extracting $WWAssociativeLinks/Property
chmextract-overflow-chmd-486: extract error on "/$WWAssociativeLinks/Property": error in data format
Extracting $OBJINST
chmextract-overflow-chmd-486: extract error on "/$OBJINST": error in data format
Extracting $OBJINST
chmextract-overflow-chmd-486: extract error on "/$OBJINST": error in data format
Extracting $OBJINST
chmextract-overflow-chmd-486: extract error on "/$OBJINST": error in data format
Extracting $OBJINST
chmextract-overflow-chmd-486: extract error on "/$OBJINST": error in data format
Extracting $OBJINST
chmextract-overflow-chmd-486: extract error on "/$OBJINST": error in data format
Extracting #TOPICS
chmextract-overflow-chmd-486: extract error on "/#TOPICS": error in data format
Extracting #URLTBL
chmextract-overflow-chmd-486: extract error on "/#URLTBL": error in data format
Extracting #URLSTR
chmextract-overflow-chmd-486: extract error on "/#URLSTR": error in data format
Extracting #TOPICS
chmextract-overflow-chmd-486: extract error on "/#TOPICS": error in data format
Extracting #TOPICS
chmextract-overflow-chmd-486: extract error on "/#TOPICS": error in data format
Extracting #TOPICS
chmextract-overflow-chmd-486: extract error on "/#TOPICS": error in data format
Extracting #URLTBL
chmextract-overflow-chmd-486: extract error on "/#URLTBL": error in data format
Extracting #SYSTEM
chmextract-overflow-chmd-486: extract error on "#SYSTEM": error in data format
Extracting #URLTBL
chmextract-overflow-chmd-486: extract error on "/#URLTBL": error in data format
Extracting #URLTBL
chmextract-overflow-chmd-486: extract error on "/#URLTBL": error in data format
Extracting #SYSTEM
chmextract-overflow-chmd-486: extract error on "#SYSTEM": error in data format
Extracting #URLSTR
chmextract-overflow-chmd-486: extract error on "/#URLSTR": error in data format
Extracting #URLSTR
chmextract-overflow-chmd-486: extract error on "/#URLSTR": error in data format
Extracting #URLSTR
chmextract-overflow-chmd-486: extract error on "/#URLSTR": error in data format
kyz commented

That's the output that would be expected. The file is not a tight, tiny example PoC. It was discovered by fuzzing, so it is a regular CHM file that has been randomly mutated enough to find the bug. It has a lot of mutations that cause extract errors, and so you see the output you do, but nonetheless chmextract still makes it through the file.

The problem is invisible; libmspack reads a few bytes beyond the end of freshly allocated memory. On most systems by default, this is not any kind of error at all. Only if we were very unlucky, and had a system that allocated memory at the end of readable/writable pages, and a few bytes after those pages were unmapped pages that cause a segfault if you try reading them, would we ever see any kind of problem.

So the only way you're going to see this problem is to use ASan to force it to be visible. Here is how you could do that:

$ git clone https://github.com/kyz/libmspack.git
$ cd libmspack/libmspack/
$ git checkout cb5d78c  # switch to the last commit before the vulnerability was fixed
$ ./autogen.sh
$ CFLAGS='-g -O2 -fsanitize=address' ./configure  # configure to use ASan
$ make
$ ./examples/chmextract /tmp/chmextract-overflow-chmd-486 
/tmp/chmextract-overflow-chmd-486
/tmp/chmextract-overflow-chmd-486: invalid section number '89'.
/tmp/chmextract-overflow-chmd-486: invalid section number '89'.
/tmp/chmextract-overflow-chmd-486: invalid section number '89'.
/tmp/chmextract-overflow-chmd-486: invalid section number '71'.
/tmp/chmextract-overflow-chmd-486: invalid section number '266694953'.
/tmp/chmextract-overflow-chmd-486: invalid section number '114'.
/tmp/chmextract-overflow-chmd-486: invalid section number '84'.
/tmp/chmextract-overflow-chmd-486: invalid section number '16259'.
/tmp/chmextract-overflow-chmd-486: invalid section number '58'.
/tmp/chmextract-overflow-chmd-486: invalid section number '67'.
/tmp/chmextract-overflow-chmd-486: invalid section number '47'.
/tmp/chmextract-overflow-chmd-486: invalid section number '48'.
/tmp/chmextract-overflow-chmd-486: invalid section number '71'.
/tmp/chmextract-overflow-chmd-486: invalid section number '266694953'.
/tmp/chmextract-overflow-chmd-486: invalid section number '114'.
/tmp/chmextract-overflow-chmd-486: invalid section number '84'.
/tmp/chmextract-overflow-chmd-486: invalid section number '16259'.
/tmp/chmextract-overflow-chmd-486: invalid section number '58'.
=================================================================
==9494==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000002500 at pc 0x7fa204faef54 bp 0x7ffc600617b0 sp 0x7ffc60060f58
READ of size 31 at 0x621000002500 thread T0
    #0 0x7fa204faef53  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xaff53)
    #1 0x7fa204cd98d3 in chmd_read_headers mspack/chmd.c:486
    #2 0x7fa204cd98d3 in chmd_real_open mspack/chmd.c:163
    #3 0x55fd6cdda76a in main examples/chmextract.c:92
    #4 0x7fa2048fbb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #5 0x55fd6cddad69 in _start (/home/kyz/libmspack/libmspack/examples/.libs/chmextract+0x1d69)

0x621000002500 is located 0 bytes to the right of 4096-byte region [0x621000001500,0x621000002500)
allocated by thread T0 here:
    #0 0x7fa204fddb50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x7fa204cd8bd9 in chmd_read_headers mspack/chmd.c:418
    #2 0x7fa204cd8bd9 in chmd_real_open mspack/chmd.c:163

SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xaff53) 
Shadow bytes around the buggy address:
  0x0c427fff8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c427fff84a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84f0: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==9494==ABORTING

... and to prove it is fixed in the current version:

$ git checkout master
$ ./autogen.sh
$ CFLAGS='-g -O2 -fsanitize=address' ./configure  # configure to use ASan
$ make
$ ./examples/chmextract /tmp/chmextract-overflow-chmd-486 
... produces the output you saw, even though ASan is enabled, because bug is fixed

Thank you for your explanation.
And
Is there any way to exploit it ?

kyz commented

There's not much to exploit.

The description of CVE-2019-1010305 is "Information Disclosure". The disclosure is whether memory starting near the end of and ending just beyond the end of a heap-based buffer matches one of four fixed strings:

  1. ::DataSpace/Storage/MSCompressed/Content
  2. ::DataSpace/Storage/MSCompressed/ControlData
  3. ::DataSpace/Storage/MSCompressed/SpanInfo
  4. ::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable

The attacker cannot arbitrarily choose the memory address nor can they learn what the memory contains, they can only determine if it matches one of four strings, which it most likely doesn't, and they need some way to see what's in the mschmd_file.sec1.{content,control,spaninfo,rtable} fields to exfiltrate that knowledge.

So it is definitely a correctness bug (libmspack should never read unallocated memory), but the security implications are minimal. Contrast it with Heartbleed, where an attacker can get a verbatim copy of up to 64kb of memory that follows where their message is stored in memory.

I can think of a worst-possible-case, but you have to go from normal behaviour, where reading a few bytes beyond allocated memory is wrong does not cause any faults, to pathological behaviour: imagine you've set up a long-running CHM-listing network service, and you've also chosen to instrument all its code with ASan. An attacker can cause the program to crash by sending this PoC file to the service, getting it to read just beyond the end of a buffer and thus trigger your intentional program-crasher.