diff options
Diffstat (limited to 'custom_mutators')
-rw-r--r-- | custom_mutators/aflpp_tritondse/README.md | 9 | ||||
-rw-r--r-- | custom_mutators/aflpp_tritondse/aflpp_tritondse.py | 78 | ||||
-rw-r--r-- | custom_mutators/symcc/README.md | 2 | ||||
-rw-r--r-- | custom_mutators/symqemu/Makefile | 14 | ||||
-rw-r--r-- | custom_mutators/symqemu/README.md | 19 | ||||
-rw-r--r-- | custom_mutators/symqemu/symqemu.c | 424 |
6 files changed, 541 insertions, 5 deletions
diff --git a/custom_mutators/aflpp_tritondse/README.md b/custom_mutators/aflpp_tritondse/README.md index 8a5dd02b..033655d2 100644 --- a/custom_mutators/aflpp_tritondse/README.md +++ b/custom_mutators/aflpp_tritondse/README.md @@ -10,8 +10,13 @@ ../../afl-cc -o ../../test-instr ../../test-instr.c mkdir -p in echo aaaa > in/in -TRITON_DSE_TARGET=../../test-instr AFL_CUSTOM_MUTATOR_ONLY=1 AFL_SYNC_TIME=1 AFL_PYTHON_MODULE=aflpp_tritondse PYTHONPATH=. ../../afl-fuzz -i in -o out -- ../../test-instr +AFL_DISABLE_TRIM=1 AFL_CUSTOM_MUTATOR_ONLY=1 AFL_SYNC_TIME=1 AFL_PYTHON_MODULE=aflpp_tritondse PYTHONPATH=. ../../afl-fuzz -i in -o out -- ../../test-instr ``` Note that this custom mutator works differently, new finds are synced -after 10-60 seconds to the fuzzing instance. +after 10-60 seconds to the fuzzing instance. This is necessary because only +C/C++ custom mutators have access to the internal AFL++ state. + +Note that you should run first with `AFL_DEBUG` for 5-10 minutes and see if +all important libraries and syscalls are hooked (look at `WARNING` and `CRITICAL` +output during the run, best use with `AFL_NO_UI=1`) diff --git a/custom_mutators/aflpp_tritondse/aflpp_tritondse.py b/custom_mutators/aflpp_tritondse/aflpp_tritondse.py index e0219f0b..58b506b6 100644 --- a/custom_mutators/aflpp_tritondse/aflpp_tritondse.py +++ b/custom_mutators/aflpp_tritondse/aflpp_tritondse.py @@ -22,14 +22,17 @@ config = None dse = None cycle = 0 count = 0 +finding = 0 hashes = set() format = SeedFormat.RAW def pre_exec_hook(se: SymbolicExecutor, state: ProcessState): global count global hashes + global finding if se.seed.hash not in hashes: hashes.add(se.seed.hash) + finding = 1 filename = out_path + "/id:" + f"{count:06}" + "," + se.seed.hash if not os.path.exists(filename): if is_debug: @@ -47,6 +50,59 @@ def pre_exec_hook(se: SymbolicExecutor, state: ProcessState): # file.write(se.seed.content) +#def rtn_open(se: SymbolicExecutor, pstate: ProcessState, pc): +# """ +# The open behavior. +# """ +# logging.debug('open hooked') +# +# # Get arguments +# arg0 = pstate.get_argument_value(0) # const char *pathname +# flags = pstate.get_argument_value(1) # int flags +# mode = pstate.get_argument_value(2) # int mode +# arg0s = pstate.memory.read_string(arg0) +# +# # Concretize the whole path name +# pstate.concretize_memory_bytes(arg0, len(arg0s)+1) # Concretize the whole string + \0 +# +# # We use flags as concrete value +# pstate.concretize_argument(1) +# +# # Use the flags to open the file in the write mode. +# mode = "" +# if (flags & 0xFF) == 0x00: # O_RDONLY +# mode = "r" +# elif (flags & 0xFF) == 0x01: # O_WRONLY +# mode = "w" +# elif (flags & 0xFF) == 0x02: # O_RDWR +# mode = "r+" +# +# if (flags & 0x0100): # O_CREAT +# mode += "x" +# if (flags & 0x0200): # O_APPEND +# mode = "a" # replace completely value +# +# if se.seed.is_file_defined(arg0s) and "r" in mode: # input file and opened in reading +# logging.info(f"opening an input file: {arg0s}") +# # Program is opening an input +# data = se.seed.get_file_input(arg0s) +# filedesc = pstate.create_file_descriptor(arg0s, io.BytesIO(data)) +# fd = filedesc.id +# else: +# # Try to open it as a regular file +# try: +# fd = open(arg0s, mode) # use the mode here +# filedesc = pstate.create_file_descriptor(arg0s, fd) +# fd = filedesc.id +# except Exception as e: +# logging.debug(f"Failed to open {arg0s} {e}") +# fd = pstate.minus_one +# +# pstate.write_register("rax", fd) # write the return value +# pstate.cpu.program_counter = pstate.pop_stack_value() # pop the return value +# se.skip_instruction() # skip the current instruction so that the engine go straight fetching the next instruction + + def init(seed): global config global dse @@ -64,6 +120,10 @@ def init(seed): is_debug = True except KeyError: pass + if is_debug: + logging.basicConfig(level=logging.WARNING) + else: + logging.basicConfig(level=logging.CRITICAL) try: foo = os.environ['AFL_CUSTOM_INFO_OUT'] out_path = foo + '/../tritondse/queue' @@ -115,10 +175,16 @@ def init(seed): dse = SymbolicExplorator(config, prog) # Add callbacks. dse.callback_manager.register_pre_execution_callback(pre_exec_hook) + #dse.callback_manager.register_function_callback("open", rtn_open) -#def fuzz(buf, add_buf, max_size): -# return b"" +def fuzz(buf, add_buf, max_size): + global finding + finding = 1 + while finding == 1: + finding = 0 + dse.step() + return b"" def queue_new_entry(filename_new_queue, filename_orig_queue): @@ -141,8 +207,14 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): dse.add_input_seed(seed) # Start exploration! #dse.step() - dse.explore() + #dse.explore() pass + +# we simulate just doing one single fuzz in the custom mutator +def fuzz_count(buf): + return 1 + + def splice_optout(): pass diff --git a/custom_mutators/symcc/README.md b/custom_mutators/symcc/README.md index 364a348e..a6839a37 100644 --- a/custom_mutators/symcc/README.md +++ b/custom_mutators/symcc/README.md @@ -5,6 +5,8 @@ This uses the symcc to find new paths into the target. Note that this is a just a proof of concept example! It is better to use the fuzzing helpers of symcc, symqemu, Fuzzolic, etc. rather than this. +Also the symqemu custom mutator is better than this. + To use this custom mutator follow the steps in the symcc repository [https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/) on how to build symcc and how to instrument a target binary (the same target diff --git a/custom_mutators/symqemu/Makefile b/custom_mutators/symqemu/Makefile new file mode 100644 index 00000000..958aec19 --- /dev/null +++ b/custom_mutators/symqemu/Makefile @@ -0,0 +1,14 @@ + +ifdef DEBUG + CFLAGS += -DDEBUG +endif + +all: symqemu-mutator.so + +CFLAGS += -O3 -funroll-loops + +symqemu-mutator.so: symqemu.c + $(CC) -g $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symqemu-mutator.so symqemu.c + +clean: + rm -f symqemu-mutator.so *.o *~ core diff --git a/custom_mutators/symqemu/README.md b/custom_mutators/symqemu/README.md new file mode 100644 index 00000000..c3071afc --- /dev/null +++ b/custom_mutators/symqemu/README.md @@ -0,0 +1,19 @@ +# custum mutator: symqemu + +This uses the symcc to find new paths into the target. + +## How to build and use + +To use this custom mutator follow the steps in the symqemu repository +[https://github.com/eurecom-s3/symqemu/](https://github.com/eurecom-s3/symqemu/) +on how to build symqemu-x86_x64 and put it in your `PATH`. + +Just type `make` to build this custom mutator. + +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symqemu/symqemu-mutator.so AFL_DISABLE_TRIM=1 afl-fuzz ...``` + +## Options + +`SYMQEMU_ALL=1` - use concolic solving on **all** queue items, not only interesting/favorite ones. + +`SYMQEMU_LATE=1` - use concolic solving only after there have been no finds for 5 minutes. diff --git a/custom_mutators/symqemu/symqemu.c b/custom_mutators/symqemu/symqemu.c new file mode 100644 index 00000000..73a1640a --- /dev/null +++ b/custom_mutators/symqemu/symqemu.c @@ -0,0 +1,424 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include "config.h" +#include "debug.h" +#include "afl-fuzz.h" +#include "common.h" + +afl_state_t *afl_struct; +static u32 debug = 0; +static u32 found_items = 0; + +#define SYMQEMU_LOCATION "symqemu" + +#define DBG(x...) \ + if (debug) { fprintf(stderr, x); } + +typedef struct my_mutator { + + afl_state_t *afl; + u32 all; + u32 late; + u8 *mutator_buf; + u8 *out_dir; + u8 *target; + u8 *symqemu; + u8 *input_file; + u32 counter; + u32 seed; + u32 argc; + u8 **argv; + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + if (getenv("AFL_DEBUG")) debug = 1; + + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + char *path = getenv("PATH"); + char *exec_name = "symqemu-x86_64"; + char *token = strtok(path, ":"); + char exec_path[4096]; + + while (token != NULL && data->symqemu == NULL) { + + snprintf(exec_path, sizeof(exec_path), "%s/%s", token, exec_name); + if (access(exec_path, X_OK) == 0) { + + data->symqemu = (u8 *)strdup(exec_path); + break; + + } + + token = strtok(NULL, ":"); + + } + + if (!data->symqemu) FATAL("symqemu binary %s not found", exec_name); + DBG("Found %s\n", data->symqemu); + + if (getenv("AFL_CUSTOM_MUTATOR_ONLY")) { + + WARNF( + "the symqemu module is not very effective with " + "AFL_CUSTOM_MUTATOR_ONLY."); + + } + + if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) { + + free(data); + perror("mutator_buf alloc"); + return NULL; + + } + + data->target = getenv("AFL_CUSTOM_INFO_PROGRAM"); + + u8 *path_tmp = getenv("AFL_CUSTOM_INFO_OUT"); + u32 len = strlen(path_tmp) + 32; + u8 *symqemu_path = malloc(len); + data->out_dir = malloc(len); + snprintf(symqemu_path, len, "%s/%s", path_tmp, SYMQEMU_LOCATION); + snprintf(data->out_dir, len, "%s/out", symqemu_path, path_tmp); + + (void)mkdir(symqemu_path, 0755); + (void)mkdir(data->out_dir, 0755); + + setenv("SYMCC_OUTPUT_DIR", data->out_dir, 1); + + data->input_file = getenv("AFL_CUSTOM_INFO_PROGRAM_INPUT"); + + u8 *tmp = NULL; + if ((tmp = getenv("AFL_CUSTOM_INFO_PROGRAM_ARGV")) && *tmp) { + + int argc = 0, index = 2; + for (u32 i = 0; i < strlen(tmp); ++i) + if (isspace(tmp[i])) ++argc; + + data->argv = (u8 **)malloc((argc + 4) * sizeof(u8 **)); + u8 *p = strdup(tmp); + + do { + + data->argv[index] = p; + while (*p && !isspace(*p)) + ++p; + if (*p) { + + *p++ = 0; + while (isspace(*p)) + ++p; + + } + + if (strcmp(data->argv[index], "@@") == 0) { + + if (!data->input_file) { + + u32 ilen = strlen(symqemu_path) + 32; + data->input_file = malloc(ilen); + snprintf(data->input_file, ilen, "%s/.input", symqemu_path); + + } + + data->argv[index] = data->input_file; + + } + + DBG("%d: %s\n", index, data->argv[index]); + index++; + + } while (*p); + + data->argv[index] = NULL; + data->argc = index; + + } else { + + data->argv = (u8 **)malloc(8 * sizeof(u8 **)); + data->argc = 2; + data->argv[2] = NULL; + + } + + data->argv[0] = data->symqemu; + data->argv[1] = data->target; + data->afl = afl; + data->seed = seed; + afl_struct = afl; + + if (getenv("SYMQEMU_ALL")) { data->all = 1; } + if (getenv("SYMQEMU_LATE")) { data->late = 1; } + if (data->input_file) { setenv("SYMCC_INPUT_FILE", data->input_file, 1); } + + DBG("out_dir=%s, target=%s, input_file=%s, argc=%u\n", data->out_dir, + data->target, + data->input_file ? (char *)data->input_file : (char *)"<stdin>", + data->argc); + + if (debug) { + + fprintf(stderr, "["); + for (u32 i = 0; i <= data->argc; ++i) + fprintf(stderr, " \"%s\"", + data->argv[i] ? (char *)data->argv[i] : "<NULL>"); + fprintf(stderr, " ]\n"); + + } + + return data; + +} + +/* No need to receive a splicing item */ +void afl_custom_splice_optout(void *data) { + + (void)(data); + +} + +/* Get unix time in milliseconds */ + +inline u64 get_cur_time(void) { + + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000); + +} + +u32 afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf, size_t buf_size) { + + if (likely((!afl_struct->queue_cur->favored && !data->all) || + afl_struct->queue_cur->was_fuzzed)) { + + return 0; + + } + + if (likely(data->late)) { + + if (unlikely(get_cur_time() - afl_struct->last_find_time <= + 10 * 60 * 1000)) { + + return 0; + + } + + } + + int pipefd[2]; + struct stat st; + + if (afl_struct->afl_env.afl_no_ui) { + + ACTF("Sending to symqemu: %s", afl_struct->queue_cur->fname); + + } + + if (!(stat(afl_struct->queue_cur->fname, &st) == 0 && S_ISREG(st.st_mode) && + st.st_size)) { + + PFATAL("Couldn't find enqueued file: %s", afl_struct->queue_cur->fname); + + } + + if (afl_struct->fsrv.use_stdin) { + + if (pipe(pipefd) == -1) { + + PFATAL( + "Couldn't create a pipe for interacting with symqemu child process"); + + } + + } + + if (data->input_file) { + + int fd = open(data->input_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + ssize_t s = write(fd, buf, buf_size); + close(fd); + DBG("wrote %zd/%zd to %s\n", s, buf_size, data->input_file); + + } + + int pid = fork(); + + if (pid == -1) return 0; + + if (likely(pid)) { + + if (!data->input_file || afl_struct->fsrv.use_stdin) { + + close(pipefd[0]); + + if (fcntl(pipefd[1], F_GETPIPE_SZ)) { + + fcntl(pipefd[1], F_SETPIPE_SZ, MAX_FILE); + + } + + ck_write(pipefd[1], buf, buf_size, data->input_file); + + close(pipefd[1]); + + } + + pid = waitpid(pid, NULL, 0); + DBG("symqemu finished executing!\n"); + + } else /* (pid == 0) */ { // child + + if (afl_struct->fsrv.use_stdin) { + + close(pipefd[1]); + dup2(pipefd[0], 0); + + } + + DBG("exec=%s\n", data->target); + if (!debug) { + + close(1); + close(2); + dup2(afl_struct->fsrv.dev_null_fd, 1); + dup2(afl_struct->fsrv.dev_null_fd, 2); + + } + + execvp((char *)data->argv[0], (char **)data->argv); + fprintf(stderr, "Executing: ["); + for (u32 i = 0; i <= data->argc; ++i) + fprintf(stderr, " \"%s\"", + data->argv[i] ? (char *)data->argv[i] : "<NULL>"); + fprintf(stderr, " ]\n"); + FATAL("Failed to execute %s %s\n", data->argv[0], data->argv[1]); + exit(-1); + + } + + /* back in mother process */ + + struct dirent **nl; + s32 i, items = scandir(data->out_dir, &nl, NULL, NULL); + found_items = 0; + char source_name[4096]; + + if (items > 0) { + + for (i = 0; i < (u32)items; ++i) { + + // symqemu output files start with a digit + if (!isdigit(nl[i]->d_name[0])) continue; + + struct stat st; + snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir, + nl[i]->d_name); + DBG("file=%s\n", source_name); + + if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) { + + ++found_items; + + } + + free(nl[i]); + + } + + free(nl); + + } + + DBG("Done, found %u items!\n", found_items); + + return found_items; + +} + +size_t afl_custom_fuzz(my_mutator_t *data, u8 *buf, size_t buf_size, + u8 **out_buf, u8 *add_buf, size_t add_buf_size, + size_t max_size) { + + struct dirent **nl; + s32 done = 0, i, items = scandir(data->out_dir, &nl, NULL, NULL); + char source_name[4096]; + + if (items > 0) { + + for (i = 0; i < (u32)items; ++i) { + + // symqemu output files start with a digit + if (!isdigit(nl[i]->d_name[0])) continue; + + struct stat st; + snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir, + nl[i]->d_name); + DBG("file=%s\n", source_name); + + if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) { + + int fd = open(source_name, O_RDONLY); + if (fd < 0) { goto got_an_issue; } + + ssize_t r = read(fd, data->mutator_buf, MAX_FILE); + close(fd); + + DBG("fn=%s, fd=%d, size=%ld\n", source_name, fd, r); + + if (r < 1) { goto got_an_issue; } + + done = 1; + --found_items; + unlink(source_name); + + *out_buf = data->mutator_buf; + return (u32)r; + + } + + free(nl[i]); + + } + + free(nl); + + } + +got_an_issue: + *out_buf = NULL; + return 0; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->mutator_buf); + free(data); + +} + |