qiling
qiling copied to clipboard
Emulation Stops When Placing Input Via ql.uc.afl_fuzz
Describe the bug When I attempt to feed input to crash a simple program via afl_fuzz place_input_callback, emulation seems to exit right after the callback is invoked. If I manually hook at the same location in program execution, and place the input, emulation continues and I observe the expected crash. The crash input file is 411 'A' characters.
Simple Test Case
#include <stdio.h>
int main(int argc, char **argv){
char buffer[80];
printf("Hi, feed me data...\n");
scanf("%s", buffer);
printf("Nom nom nom nom: %s\n", buffer);
return 0;
}
Placing input via afl_fuzz
#!/usr/bin/env python3
import os,sys
# This is new. Instead of unicorn, we import unicornafl. It's the same Uc with some new `afl_` functions
import unicornafl
# Make sure Qiling uses our patched unicorn instead of it's own, second so without instrumentation!
unicornafl.monkeypatch()
from qiling import *
from qiling.const import *
from qiling.extensions import pipe
from IPython import embed
def main(input_file):
print("Creating Mock stdin")
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
print("Initializing Qiling")
ql = Qiling(["./main"], "./rootfs",
verbose=QL_VERBOSE.DISASM,
stdin=mock_stdin)
def place_input_callback(uc, input, _, data):
print("Placing input")
bytes = ql.os.stdin.write(input)
print(f"Wrote {bytes} bytes")
def start_afl(_ql: Qiling):
"""
Callback from inside
"""
# We start our AFL forkserver or run once if AFL is not available.
# This will only return after the fuzzing stopped.
if not _ql.uc.afl_fuzz(input_file=input_file,
place_input_callback=place_input_callback,
exits=[ql.os.exit_point]):
os._exit(0) # that's a looot faster than tidying up.
# get image base address
ba = ql.loader.images[0].base
# make process crash whenever __stack_chk_fail@plt is about to be called.
# this way afl will count stack protection violations as crashes
ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x11e0)
# # set a hook on main() to let unicorn fork and start instrumentation
ql.hook_address(callback=start_afl, address=ba + 0x1169)
ql.run()
os._exit(0)
if __name__ == "__main__":
main(sys.argv[1])
Output
$ python3 fuzz.py afl_inputs/crash_input
[=] 00007fffb7df7b7d [[syscall_mmap] + 0x021b7d] 48 8b 05 24 93 3c 00 mov rax, qword ptr [rip + 0x3c9324]
[=] 00007fffb7df7b84 [[syscall_mmap] + 0x021b84] 48 8b 74 24 08 mov rsi, qword ptr [rsp + 8]
[=] 00007fffb7df7b89 [[syscall_mmap] + 0x021b89] 8b 7c 24 14 mov edi, dword ptr [rsp + 0x14]
[=] 00007fffb7df7b8d [[syscall_mmap] + 0x021b8d] 48 8b 10 mov rdx, qword ptr [rax]
[=] 00007fffb7df7b90 [[syscall_mmap] + 0x021b90] 48 8b 44 24 18 mov rax, qword ptr [rsp + 0x18]
[=] 00007fffb7df7b95 [[syscall_mmap] + 0x021b95] ff d0 call rax
[=] 0000555555555169 [main + 0x000169] 55 push rbp
Placing input
Wrote 411 bytes
[=] 0000555555555169 [main + 0x000169] 55 push rbp
$
Placing input manually
#!/usr/bin/env python3
import os,sys
# This is new. Instead of unicorn, we import unicornafl. It's the same Uc with some new `afl_` functions
import unicornafl
# Make sure Qiling uses our patched unicorn instead of it's own, second so without instrumentation!
unicornafl.monkeypatch()
from qiling import *
from qiling.const import *
from qiling.extensions import pipe
from IPython import embed
def main(input_file):
print("Creating Mock stdin")
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
print("Initializing Qiling")
ql = Qiling(["./main"], "./rootfs",
verbose=QL_VERBOSE.DISASM,
stdin=mock_stdin)
def place_input_callback_no_afl(ql):
print("Placing input")
with open(input_file, "rb") as f:
# embed()
bytes = ql.os.stdin.write(f.read())
print(f"Wrote {bytes} bytes")
# get image base address
ba = ql.loader.images[0].base
# make process crash whenever __stack_chk_fail@plt is about to be called.
# this way afl will count stack protection violations as crashes
ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x11e0)
# # set a hook on main() to let unicorn fork and start instrumentation
ql.hook_address(callback=place_input_callback_no_afl, address=ba + 0x1169)
ql.run()
os._exit(0)
if __name__ == "__main__":
main(sys.argv[1])
Output
$ python3 fuzz_no_afl.py afl_inputs/crash_input
[=] 00007fffb7e3af2b [[syscall_mmap] + 0x064f2b] 64 48 33 0c 25 28 00 00 00 xor rcx, qword ptr fs:[0x28]
[=] 00007fffb7e3af34 [[syscall_mmap] + 0x064f34] 75 08 jne 0x7fffb7e3af3e
[=] 00007fffb7e3af36 [[syscall_mmap] + 0x064f36] 48 81 c4 d8 00 00 00 add rsp, 0xd8
[=] 00007fffb7e3af3d [[syscall_mmap] + 0x064f3d] c3 ret
[=] 00005555555551cc [main + 0x0001cc] b8 00 00 00 00 mov eax, 0
[=] 00005555555551d1 [main + 0x0001d1] 48 8b 55 f8 mov rdx, qword ptr [rbp - 8]
[=] 00005555555551d5 [main + 0x0001d5] 64 48 2b 14 25 28 00 00 00 sub rdx, qword ptr fs:[0x28]
[=] 00005555555551de [main + 0x0001de] 74 05 je 0x5555555551e5
[=] 00005555555551e0 [main + 0x0001e0] e8 5b fe ff ff call 0x555555555040
Aborted (core dumped)
$
Less Verbose Comparison
$ python3 fuzz_no_afl.py afl_inputs/crash_input
[+] 0x00007ffff7df1f41: mmap(addr = 0x7fffb81c3000, length = 0x3ae0, prot = 0x3, flags = 0x32, fd = 0xffffffff, pgoffset = 0x0) = 0x7fffb81c3000
[+] 0x00007ffff7df1ed5: close(fd = 0x3) = 0x0
[+] mmap - mapping needed for 0x0
[+] mmap - addr range 0x7fffb81c7000 - 0x7fffb81c8fff:
[+] 0x00007ffff7df1f41: mmap(addr = 0x0, length = 0x2000, prot = 0x3, flags = 0x22, fd = 0xffffffff, pgoffset = 0x0) = 0x7fffb81c7000
[+] 0x00007ffff7dd6022: arch_prctl(code = 0x1002, addr = 0x7fffb81c7f00) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x7fffb81bd000, mlen = 0x4000, prot = 0x1) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x555555557000, mlen = 0x1000, prot = 0x1) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x7ffff7ffc000, mlen = 0x1000, prot = 0x1) = 0x0
Placing input
Wrote 411 bytes
[+] fstat write completed
[+] 0x00007fffb7ee57c1: fstat(fd = 0x1, buf_ptr = 0x80000000dbb0) = 0x0
[+] 0x00007fffb7eec4b7: brk(inp = 0x0) = 0x55555555b000
[+] 0x00007fffb7eec4b7: brk(inp = 0x55555557c000) = 0x55555557c000
[+] write() CONTENT: 'Hi, feed me data...\n'
Hi, feed me data...
[+] 0x00007fffb7ee6152: write(fd = 0x1, buf = 0x55555555b260, count = 0x14) = 0x14
[+] fstat write completed
[+] 0x00007fffb7ee57c1: fstat(fd = 0x0, buf_ptr = 0x80000000d430) = 0x0
[+] read() CONTENT: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
[+] 0x00007fffb7ee607f: read(fd = 0x0, buf = 0x55555555b670, length = 0x400) = 0x19b
[+] write() CONTENT: 'Nom nom nom nom: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
Nom nom nom nom: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[+] 0x00007fffb7ee6152: write(fd = 0x1, buf = 0x55555555b260, count = 0x1ac) = 0x1ac
Aborted (core dumped)
$ python3 fuzz.py afl_inputs/crash_input
[+] mmap - MAP_FIXED, mapping not needed
[+] mmap - addr range 0x7fffb81c3000 - 0x7fffb81c6fff:
[+] 0x00007ffff7df1f41: mmap(addr = 0x7fffb81c3000, length = 0x3ae0, prot = 0x3, flags = 0x32, fd = 0xffffffff, pgoffset = 0x0) = 0x7fffb81c3000
[+] 0x00007ffff7df1ed5: close(fd = 0x3) = 0x0
[+] mmap - mapping needed for 0x0
[+] mmap - addr range 0x7fffb81c7000 - 0x7fffb81c8fff:
[+] 0x00007ffff7df1f41: mmap(addr = 0x0, length = 0x2000, prot = 0x3, flags = 0x22, fd = 0xffffffff, pgoffset = 0x0) = 0x7fffb81c7000
[+] 0x00007ffff7dd6022: arch_prctl(code = 0x1002, addr = 0x7fffb81c7f00) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x7fffb81bd000, mlen = 0x4000, prot = 0x1) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x555555557000, mlen = 0x1000, prot = 0x1) = 0x0
[+] 0x00007ffff7df1ff5: mprotect(start = 0x7ffff7ffc000, mlen = 0x1000, prot = 0x1) = 0x0
Placing input
Wrote 411 bytes
Expected behavior I expect the program to crash under afl_fuzz. This manifests as AFL successfully starting, but not discovering program flaws since the emulation halts prior to the target processing malicious input.
Main function listing (so addresses are included)
That works as intended. You need afl-fuzz
to spin up an AFL server.
@wtdcode the program doesn't crash when emulating under afl_fuzz though, which seems incorrect. The place_input_callback halts emulation if the afl server is not running?
When I emulate with afl-fuzz, I get no crashes, despite a crashing input being a part of the corpus. My hunch is that the program quits emulating before processing the input, and as such never crashes.
@wtdcode the program doesn't crash when emulating under afl_fuzz though, which seems incorrect. The place_input_callback halts emulation if the afl server is not running?
When I emulate with afl-fuzz, I get no crashes, despite a crashing input being a part of the corpus. My hunch is that the program quits emulating before processing the input, and as such never crashes.
I suppose you are using unicornafl 2.0, right?
In [1]: import unicornafl as uc
In [2]: uc.__version__
Out[2]: '1.0.3'
No, should I be? I can update and report back.
In [1]: import unicornafl as uc In [2]: uc.__version__ Out[2]: '1.0.3'
No, should I be? I can update and report back.
Yes, the new unicornafl 2.x could have a max of 40% speedup in some cases.
Hello, does the fuzzing works properly with you when you update to unicornafl2 ???
Close for now.
We updated the codebase for Qiling and Unicorn since this issue being posted.
Feel free to try the latest version.