Battelle / afl-unicorn

afl-unicorn lets you fuzz any piece of binary that can be emulated by Unicorn Engine.

Home Page:https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fork server handshake failed - UnicornLoader script

H0r53 opened this issue · comments

commented

I've been following the AFL Unicorn Guides and I'm trying to recreate the Unicorn dumper/loader functionality on the sample binary provided here. I compiled the aforementioned binary as a 32-bit ELF executable and I created a simple custom test harness based on the Loader Test Harness and the Raw Binary Test Harness.

I used the GDB helper script to dump the process context of simple_target at main. I've loaded the context successfully and I've also used the mem_write method to place sample data at 0x00300000 to be mutated. I was very careful to set the START_ADDRESS and END_ADDRESS for Unicorn based on the appropriate values associated with the dumped context.

After setting everything up, I ran the following command:

../../afl-fuzz -U -m none -i ./sample_inputs/ -o ./outputs -- python driver.py @@

Unfortunately, I receive an error on spinning up the fork server:

[*] Spinning up the fork server...

[-] Hmm, looks like the target binary terminated before we could complete a
    handshake with the injected code. Perhaps there is a horrible bug in the
    fuzzer. Poke <lcamtuf@coredump.cx> for troubleshooting tips.

[-] PROGRAM ABORT : Fork server handshake failed
         Location : init_forkserver(), afl-fuzz.c:2258

If I run the script standalone, I don't receive any issues and emulation is successful:

python driver.py -d UnicornContext_20191031_101427/ sample_inputs/sample1.bin

Why is this error occurring? This is the most simple proof of concept I can think of for getting started with AFL Unicorn, and I've based everything on the examples available to me. I've condensed my test harness code (provided below) for reference. The START and END address are relative to my context dump, but they should be easily reproducible if you use GDB to break simple_target on main and dump the context from there with the unicorn dumper helper script.

import argparse
from unicorn import *
from unicorn.x86_const import *  # TODO: Set correct architecture here as necessary
import unicorn_loader
unicorn_heap = None
START_ADDRESS = 0x565554ed # TODO: Set start address here
END_ADDRESS   = 0x5655558d # TODO: Set end address here
STACK_ADDRESS = 0x00200000  # Address of the stack (arbitrarily chosen)
STACK_SIZE    = 0x00010000  # Size of the stack (arbitrarily chosen)
DATA_ADDRESS  = 0x00300000  # Address where mutated data will be placed
DATA_SIZE_MAX = 0x00010000  # Maximum allowable size of mutated data
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('context_dir', type=str, help="Directory containing process context")
    parser.add_argument('input_file', type=str, help="Path to the file containing the mutated input content")
    parser.add_argument('-d', '--debug', default=False, action="store_true", help="Dump trace info")
    args = parser.parse_args()
    print("Loading context from {}".format(args.context_dir))
    uc = unicorn_loader.AflUnicornEngine(args.context_dir, enable_trace=args.debug, debug_print=args.debug)
    global unicorn_heap
    unicorn_heap = unicorn_loader.UnicornSimpleHeap(uc, debug_print=True)
    uc.hook_add(UC_HOOK_CODE, unicorn_hook_instruction)
    print("Starting the forkserver by executing 1 instruction")
    try:
        uc.emu_start(START_ADDRESS, 0, 0, count=1)
    except UcError as e:
        print("ERROR: Failed to execute a single instruction (error: {})!".format(e))
        return
    if args.input_file:
        print("Loading input content from {}".format(args.input_file))
        input_file = open(args.input_file, 'rb')
        input_content = input_file.read()
        input_file.close()
        buf_addr = unicorn_heap.malloc(len(input_content))
        uc.mem_write(buf_addr, input_content)
        print("Allocated mutated input buffer @ 0x{0:016x}".format(buf_addr))

    # Fill data at DATA_ADDRESS for mutating
    uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
    uc.mem_write(DATA_ADDRESS, ('\x00' * DATA_SIZE_MAX).encode())
    print("Executing from 0x{0:016x} to 0x{1:016x}".format(START_ADDRESS, END_ADDRESS))
    try:
        result = uc.emu_start(START_ADDRESS, END_ADDRESS, timeout=0, count=0)
    except UcError as e:
        print("Execution failed with error: {}".format(e))
        uc.dump_regs()
        uc.force_crash(e)
    print("Final register state:")
    uc.dump_regs()
    print("Done.")

if __name__ == "__main__":
    main()
commented

It looks like the issue was related to my using of Python3 by default when AFL Unicorn support seems to focus on Python2. Due to the upcoming depreciation of Python2, will Python3 have better support in the future?