libimobiledevice / libplist

A library to handle Apple Property List format in binary or XML
https://libimobiledevice.org
GNU Lesser General Public License v2.1
548 stars 304 forks source link

OOB access in `plist_from_memory` #244

Closed oliverchang closed 11 months ago

oliverchang commented 11 months ago

This was found using this fuzz target:

#include <plist/plist.h>
#include <stdio.h>

extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size)
{
    plist_t root_node = NULL;
    plist_format_t format;
    plist_from_memory(reinterpret_cast<const char*>(data), size, &root_node, reinterpret_cast<plist_format_t*>(&format));
    plist_free(root_node);

    return 0;
}

If we pass an input containing a single whitespace character, we get the following crash.

$ echo > input
$ ./fuzzer input
=================================================================
==1593913==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000031 at pc 0x55edd8892a82 bp 0x7ffd5a7d7010 sp 0x7ffd5a7d7008
READ of size 1 at 0x602000000031 thread T0
    #0 0x55edd8892a81 in plist_from_memory /usr/local/google/home/ochang/libplist/src/plist.c:225:13
    #1 0x55edd88925a3 in LLVMFuzzerTestOneInput (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x1305a3) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #2 0x55edd87b8463 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x56463) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #3 0x55edd87a14ef in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x3f4ef) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #4 0x55edd87a7376 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x45376) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #5 0x55edd87d2302 in main (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x70302) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #6 0x7fb7385666c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #7 0x7fb738566784 in __libc_start_main csu/../csu/libc-start.c:360:3
    #8 0x55edd879b990 in _start (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x39990) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)

0x602000000031 is located 0 bytes to the right of 1-byte region [0x602000000030,0x602000000031)
allocated by thread T0 here:
    #0 0x55edd888ff4d in operator new[](unsigned long) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x12df4d) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #1 0x55edd87b8372 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x56372) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #2 0x55edd87a14ef in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x3f4ef) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #3 0x55edd87a7376 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x45376) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #4 0x55edd87d2302 in main (/usr/local/google/home/ochang/libplist/fuzz/a.out+0x70302) (BuildId: c693d0b93035822c68e876f752fea4035ceaa1ed)
    #5 0x7fb7385666c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/local/google/home/ochang/libplist/src/plist.c:225:13 in plist_from_memory
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 01 fa fa fa[01]fa fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: 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
==1593913==ABORTING

The root cause looks like there's some bounds checking that needs to happen after whitespace characters are skipped here: https://github.com/libimobiledevice/libplist/blob/c46afc87ad605936ebcb6c03d3f309f818fd6f09/src/plist.c#L224

nikias commented 11 months ago

Thanks for reporting. I was about to say, interesting that clusterfuzz didn't catch that one yet, but plist_from_memory isn't part of the harness.

oliverchang commented 11 months ago

Thanks for the response!

And heh, this is an auto-generated target we got as part of https://security.googleblog.com/2023/08/ai-powered-fuzzing-breaking-bug-hunting.html. We're hoping to roll this out more broadly next year for all OSS-Fuzz projects.

nikias commented 11 months ago

Nice! Fixed the OOB access with 8487d23fd2ab5683d631fd41e5f6a2f5a44d867a.