Playing with Libfuzzer

Posted on

I really want to be fuzzing more in general. Towards the end of last year I decided to play a bit with libfuzzer. Here is my quick test from the tutorial.

The command

  wget https://github.com/google/fuzzing/raw/master/tutorial/libFuzzer/fuzz_me.cc
  /opt/homebrew/Cellar/llvm/17.0.6/bin/clang++ -g -fsanitize=address,fuzzer fuzz_me.cc
  ./a.out

The output

--2023-12-21 16:13:34--  https://github.com/google/fuzzing/raw/master/tutorial/libFuzzer/fuzz_me.cc
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/google/fuzzing/master/tutorial/libFuzzer/fuzz_me.cc [following]
--2023-12-21 16:13:35--  https://raw.githubusercontent.com/google/fuzzing/master/tutorial/libFuzzer/fuzz_me.cc
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 336 [text/plain]
Saving to: ‘fuzz_me.cc.2’

     0K                                                       100% 4.27M=0s

2023-12-21 16:13:35 (4.27 MB/s) - ‘fuzz_me.cc.2’ saved [336/336]

ld: warning: ignoring duplicate libraries: '-lc++'
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1639630457
INFO: Loaded 1 modules   (7 inline 8-bit counters): 7 [0x1047880e8, 0x1047880ef), 
INFO: Loaded 1 PC tables (7 PCs): 7 [0x1047880f0,0x104788160), 
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2	INITED cov: 3 ft: 3 corp: 1/1b exec/s: 0 rss: 48Mb
#4	NEW    cov: 4 ft: 4 corp: 2/4b lim: 4 exec/s: 0 rss: 48Mb L: 3/3 MS: 2 CopyPart-InsertByte-
#462	NEW    cov: 5 ft: 5 corp: 3/12b lim: 8 exec/s: 0 rss: 48Mb L: 8/8 MS: 3 InsertRepeatedBytes-CrossOver-CMP- DE: "F\000"-
#487	REDUCE cov: 5 ft: 5 corp: 3/8b lim: 8 exec/s: 0 rss: 49Mb L: 4/4 MS: 5 CopyPart-ChangeBinInt-CopyPart-PersAutoDict-EraseBytes- DE: "F\000"-
#563	REDUCE cov: 5 ft: 5 corp: 3/7b lim: 8 exec/s: 0 rss: 49Mb L: 3/3 MS: 1 EraseBytes-
#7331	REDUCE cov: 6 ft: 6 corp: 4/11b lim: 74 exec/s: 0 rss: 49Mb L: 4/4 MS: 3 EraseBytes-PersAutoDict-ChangeByte- DE: "F\000"-
#8189	REDUCE cov: 6 ft: 6 corp: 4/10b lim: 80 exec/s: 0 rss: 49Mb L: 3/3 MS: 3 CopyPart-CrossOver-EraseBytes-
#21815	REDUCE cov: 7 ft: 7 corp: 5/79b lim: 212 exec/s: 0 rss: 50Mb L: 69/69 MS: 1 InsertRepeatedBytes-
#22007	REDUCE cov: 7 ft: 7 corp: 5/67b lim: 212 exec/s: 0 rss: 50Mb L: 57/57 MS: 2 ChangeByte-EraseBytes-
#22113	REDUCE cov: 7 ft: 7 corp: 5/43b lim: 212 exec/s: 0 rss: 50Mb L: 33/33 MS: 1 EraseBytes-
#22224	REDUCE cov: 7 ft: 7 corp: 5/36b lim: 212 exec/s: 0 rss: 50Mb L: 26/26 MS: 1 CrossOver-
#22291	REDUCE cov: 7 ft: 7 corp: 5/31b lim: 212 exec/s: 0 rss: 50Mb L: 21/21 MS: 2 ShuffleBytes-EraseBytes-
#22390	REDUCE cov: 7 ft: 7 corp: 5/27b lim: 212 exec/s: 0 rss: 50Mb L: 17/17 MS: 4 ChangeBinInt-ChangeBinInt-ChangeByte-EraseBytes-
#22524	REDUCE cov: 7 ft: 7 corp: 5/26b lim: 212 exec/s: 0 rss: 50Mb L: 16/16 MS: 4 ChangeByte-ChangeBit-ChangeBit-EraseBytes-
#22635	REDUCE cov: 7 ft: 7 corp: 5/23b lim: 212 exec/s: 0 rss: 50Mb L: 13/13 MS: 1 EraseBytes-
#22871	REDUCE cov: 7 ft: 7 corp: 5/17b lim: 212 exec/s: 0 rss: 50Mb L: 7/7 MS: 1 EraseBytes-
#22934	REDUCE cov: 7 ft: 7 corp: 5/14b lim: 212 exec/s: 0 rss: 50Mb L: 4/4 MS: 3 ShuffleBytes-CrossOver-EraseBytes-
=================================================================
==20738==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000090ff3 at pc 0x000104746860 bp 0x00016b6ba850 sp 0x00016b6ba848
READ of size 1 at 0x602000090ff3 thread T0
    #0 0x10474685c in FuzzMe(unsigned char const*, unsigned long) fuzz_me.cc:9
    #1 0x1047468cc in LLVMFuzzerTestOneInput fuzz_me.cc:13
    #2 0x104762240 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:614
    #3 0x104761b28 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) FuzzerLoop.cpp:516
    #4 0x104763274 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:760
    #5 0x1047640b4 in fuzzer::Fuzzer::Loop(std::__1::vector<fuzzer::SizedFile, std::__1::allocator<fuzzer::SizedFile>>&) FuzzerLoop.cpp:905
    #6 0x104753c54 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:911
    #7 0x10477dd88 in main FuzzerMain.cpp:20
    #8 0x18dad10dc  (<unknown module>)
    #9 0xc7377ffffffffffc  (<unknown module>)

0x602000090ff3 is located 0 bytes after 3-byte region [0x602000090ff0,0x602000090ff3)
allocated by thread T0 here:
    #0 0x105032474 in _Znam+0x6c (libclang_rt.asan_osx_dynamic.dylib:arm64+0x62474)
    #1 0x104762154 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:598
    #2 0x104761b28 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) FuzzerLoop.cpp:516
    #3 0x104763274 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:760
    #4 0x1047640b4 in fuzzer::Fuzzer::Loop(std::__1::vector<fuzzer::SizedFile, std::__1::allocator<fuzzer::SizedFile>>&) FuzzerLoop.cpp:905
    #5 0x104753c54 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:911
    #6 0x10477dd88 in main FuzzerMain.cpp:20
    #7 0x18dad10dc  (<unknown module>)
    #8 0xc7377ffffffffffc  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow fuzz_me.cc:9 in FuzzMe(unsigned char const*, unsigned long)
Shadow bytes around the buggy address:
  0x602000090d00: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x602000090d80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x602000090e00: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x602000090e80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fd
  0x602000090f00: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fd
=>0x602000090f80: fa fa fd fa fa fa fd fd fa fa fd fa fa fa[03]fa
  0x602000091000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x602000091080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x602000091100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x602000091180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x602000091200: 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
==20738==ABORTING
MS: 2 InsertByte-EraseBytes-; base unit: aea2e3923af219a8956f626558ef32f30a914ebc
0x46,0x55,0x5a,
FUZ
artifact_prefix='./'; Test unit written to ./crash-0eb8e4ed029b774d80f2b66408203801cb982a60
Base64: RlVa
/bin/bash: line 3: 20738 Abort trap: 6           ./a.out