┌───────────────────────┐
                                                         ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
                                                         │ █   █ █ █ █   █       │
                                                         │ █   █ █ █ █▀▀▀▀       │
                                                         │ █   █   █ █     ▄     │
                                                         │                 ▄▄▄▄▄ │
                                                         │                 █   █ │
                                                         │                 █   █ │
                                                         │                 █▄▄▄█ │
                                                         │                 ▄   ▄ │
                                                         │                 █   █ │
                                                         │                 █   █ │
                                                         │                 █▄▄▄█ │
                                                         │                 ▄▄▄▄▄ │
                                                         │                   █   │
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