fuzzercorn icon indicating copy to clipboard operation
fuzzercorn copied to clipboard

Problem while fuzzing with qiling

Open SamNzo opened this issue 2 years ago • 4 comments

I am trying to use fuzzercorn with qiling with this example: https://github.com/qilingframework/qiling/blob/master/examples/fuzzing/linux_x8664/libfuzzer_x8664_linux.py

I first installed fuzzercorn with pip but when executing libfuzzer_x8664_linux.py I got the following

WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".

It expected an input as if I executed the binary without fuzzing and then returned a seg fault

To fix it I tried to clone the fuzzercorn repo, build libfuzzercorn.so and create a sym link between it and the one I installed with pip

This time when executing libfuzzer_x8664_linux.py I still had this waiting for an input

WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".

But when I entered one it looked like libfuzzer was working

A
A�Y���
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1757911264
INFO: 4096 Extra Counters
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 ft: 2 corp: 1/1b exec/s: 0 rss: 84Mb
#2048	pulse  ft: 2 corp: 1/1b lim: 21 exec/s: 682 rss: 86Mb
#4096	pulse  ft: 2 corp: 1/1b lim: 43 exec/s: 682 rss: 87Mb
#8192	pulse  ft: 2 corp: 1/1b lim: 80 exec/s: 630 rss: 89Mb
#16384	pulse  ft: 2 corp: 1/1b lim: 163 exec/s: 630 rss: 93Mb

However it keeps doing this until it eventually runs out of memory without finding any crash

I'm confused, do you know what am I doing wrong ?

thx

SamNzo avatar Apr 27 '23 15:04 SamNzo

Hey, thanks for your interest! The symbol seems from sanitizers and shouldn't be used with fuzzercorn actually. i.e. you could safely ignore them.

wtdcode avatar Apr 27 '23 18:04 wtdcode

Thank you, this answers my first question !

But I still have two more:

  • Is it normal that when executing libfuzzer_x8664_linux.py I then have to manually enter an input ? shouldn't it be done automatically while fuzzing ?
  • The output from libFuzzer shows that it never gets any new coverage and cant find any crash
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1269338389
INFO: 4096 Extra Counters
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 ft: 186 corp: 1/1b exec/s: 0 rss: 86Mb
#2048   pulse  ft: 186 corp: 1/1b lim: 21 exec/s: 682 rss: 87Mb
#4096   pulse  ft: 186 corp: 1/1b lim: 43 exec/s: 682 rss: 88Mb
#8192   pulse  ft: 186 corp: 1/1b lim: 80 exec/s: 682 rss: 90Mb
#16384  pulse  ft: 186 corp: 1/1b lim: 163 exec/s: 655 rss: 95Mb
#32768  pulse  ft: 186 corp: 1/1b lim: 325 exec/s: 642 rss: 103Mb
#65536  pulse  ft: 186 corp: 1/1b lim: 652 exec/s: 624 rss: 120Mb
#131072 pulse  ft: 186 corp: 1/1b lim: 1300 exec/s: 648 rss: 155Mb

It tried to modify the addresses in libfuzzer_x8664_linux.py for fun, ___stack_chk_fail and main with the ones I had in IDA but it doesnt change anything

SamNzo avatar Apr 28 '23 08:04 SamNzo

This doesn't seem correct indeed. Could you share the full reproduction case, including the script and binary?

wtdcode avatar Apr 28 '23 18:04 wtdcode

The script is the following:

#!/usr/bin/python3

from fuzzercorn import *
from unicorn import *
from qiling import Qiling
from qiling.extensions import pipe

import sys, os, ctypes

class SimpleFuzzer:

    def run(self):
        ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux", console=False)
        ba = ql.loader.images[0].base
        try:
            # Only instrument the function `fun`, so we don't need to instrument libc and ld
            FuzzerCornFuzz(ql.uc, sys.argv, [ql.os.exit_point], self.place_input, self.init, UserData=ql, Ranges=[(ba+0x11c9, ba+0x1274)], CountersCount=4096)
        except Exception as ex:
            os.abort() # Quick exit
    
    def place_input(self, uc: Uc, data: ctypes.Array, ql: Qiling):
        # Restore from snapshot
        ql.restore(self.snapshot)
        ql.os.stdin = pipe.SimpleInStream(1)
        ql.os.stdin.write(bytes(data))
        return 1

    def init(self, uc: Uc, argv: list, ql: Qiling):
        ba = ql.loader.images[0].base
        # Call os.abort when ___stack_chk_fail is called (stack overflow)
        ql.hook_address(callback=lambda x: os.abort(), address=ba+0x126e) # ___stack_chk_fail
        # Execute binary until given address
        ql.run(end=ba+0x1293) # Run to main.

        # Save a snapshot.
        self.snapshot = ql.save()
        return 0
    

if __name__ == "__main__":
    # chmod +x ./libfuzzer_x8664_linux.py
    # ./libfuzzer_x8664_linux.py -jobs=6 -workers=6
    SimpleFuzzer().run()

The binary is: https://github.com/qilingframework/qiling/blob/master/examples/fuzzing/linux_x8664/x8664_fuzz

SamNzo avatar May 03 '23 08:05 SamNzo