tmp.0ut

                                                             ┌───────────────────────┐
                                                             ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
                                                             │ █   █ █ █ █   █       │
                                                             │ █   █ █ █ █▀▀▀▀       │
                                                             │ █   █   █ █     ▄     │
                                                             │                 ▄▄▄▄▄ │
                                                             │                 █   █ │
                                                             │                 █   █ │
                                                             │                 █▄▄▄█ │
                                                             │                 ▄   ▄ │
                                                             │                 █   █ │
                                                             │                 █   █ │
                                                             │                 █▄▄▄█ │
                                                             │                 ▄▄▄▄▄ │
                                                             │                   █   │
    Fuzzing Radare2 For 0days In About 30 Lines Of Code      │                   █   │
    ~ Architect & S01den                                     └───────────────────█ ──┘

    --- Abstract ---

    Radare2 is a well-known open-source framework for reverse-engineering and binary
    analysis.

    This kind of tool is pretty interesting to analyse, searching for vulnerabilities,
    since they are used in fields such as malware analysis.

    In this paper we'll explain how we discovered two bugs (CVE-2020-16269 and
    CVE-2020-17487) from scratch, by writting our own -dumb- fuzzer and doing a bit
    of reverse-engineering.

    In a first part, we'll explain how we fuzzed radare2 and in a second part, we'll
    see how we used the crashes found by fuzzing in order to analyse, isolate and
    reproduce bugs, by taking as example the ELF related bug (CVE-2020-16269).

    --- Fuzzing ---

    In order to find the two vulnerabilities, we applied dumb fuzzing to our target.
    The key factor when doing dumb fuzzing, is having a diverse corpus in terms of
    code coverage.

    We chose to use the testbins repo from Radare2[0].

    During fuzzing we found crashes within 30 minutes, in several different file
    formats. Of the formats, interesting to us, were PE and ELF, the two most used
    executable formats.

    Without further delay, here is a tiny version of our fuzzer.

    ----------------------------------- CUT-HERE -------------------------------------
    import glob;import random;import subprocess;import hashlib

    def harness(d):
        tf = open("wdir/tmp", "wb")
        tf.write(d)
        tf.close()
        try:
            p = subprocess.run(['r2','-qq', '-AA','wdir/tmp'], stdin=None, timeout=10)
        except:
            return
        try:
            p.check_returncode()
        except:
            print(f"Proc exited with code {p.returncode}")
            fh = hashlib.sha256(d).hexdigest()

            dump = open(f'cdir/crash_{fh}', 'wb')
            dump.write(d);dump.close()

    def mutate(data):
        mutable_bytes = bytearray(data)
        for a in range(10):
            r = random.randint(0, len(mutable_bytes)-1)
            mutable_bytes[r] = random.randint(0,254)

        return mutable_bytes

    if __name__ == '__main__':
        fs = glob.glob("corpus/*")
        while True:
            f = open(random.choice(fs), 'rb').read()
            harness(mutate(f))
    ----------------------------------------------------------------------------------

    --- Exploitation ---

    Having a few sample that will make Radare2 crash, letus look at the reason behind
    the crash.

    The first one is an ELF, a mutated version of dwarftest, a sample file which holds
    DWARF informations.

    ==================================================================================
    $ file dwarftest
    ---> dwarftest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
    linked, ...,with debug_info, not stripped
    ==================================================================================

    To find out which byte triggers the bug, we analyze the offending sample loaded
    with Radare2 using a debugger.

    Alternatively it is also viable to diff the original and mutated sample to find
    the offending byte(s).

    We can do that easily thanks to radiff2:
    ==================================================================================
    $ radiff2 bins/src/dwarftest mutated_dwarftest
    0x000010e1 00 => 01 0x000010e1
    ==================================================================================

    This offset in the file is part of the DWARF structure. This is only true for
    binaries that already have DWARF information attached, but we should be able to
    craft malformed DWARF info and inject it into any ELF.

    To figure out why our DWARF info upsets Radare2 we can take a look with objdump:
    ==================================================================================
    $ objdump --dwarf=info mutated_dwarftest
    ...
    		<4c> DW_AT_name :objdump: WARNING: the DW_FORM_strp shift is too
    		large: 164 (indirect string, shift: 0x164): <shift too large>
    ...
    ==================================================================================

    Well, we’re almost done.

    Now, just need to look how we can exploit it. To do that, we just have to look at
    the backtrace of a crash with gdb and then, analyse the source code of the
    function (radare2 being fortunately an open-source project) where the bug is
    triggered.

    The faulty line is in the function parse_typedef:
    ==================================================================================
    name = strdup (value->string.content);
    ==================================================================================

    This triggers a null pointer dereference when the duplicated string is NULL, and
    without going into details, we figured out thanks to the forbidden power of
    reverse engineering that it’s the case when a shift in DW_AT_name is too large.

    Now, it’s time to write a script which can modify any ELF to trigger the bug.
    In appendix, you can find the full exploit, containing the exploitation of the
    PE bug (CVE-2020-17487, which also simply makes radare2 unable to load the binary)

    --- Conclusion ---

    We hope that you enjoyed this paper.

    Now, you know that it isn't that hard to find bugs in widely-used tools. So now,
    try to find it yourself (and especially in reverse-engineering tools) !

    Even if the bug isn't exploitable in another way than a DoS, crashing a reverse
    engineering tool when loading a binary still useful...

    --- Notes & References ---

    [0] https://github.com/radareorg/radare2-testbins

    --- Appendix ---

    - Exploit POC