Skip to content

Fork server handshake failed - UnicornLoader script #18

Description

@H0r53

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()

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions