Fork server handshake failed - UnicornLoader script
H0r53 opened this issue · comments
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()
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?