diff options
-rw-r--r-- | custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.cc | 8 | ||||
-rw-r--r-- | custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.h | 2 | ||||
-rw-r--r-- | docs/Changelog.md | 7 | ||||
-rw-r--r-- | docs/best_practices.md | 21 | ||||
-rw-r--r-- | frida_mode/GNUmakefile | 28 | ||||
-rw-r--r-- | frida_mode/include/instrument.h | 3 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument.c | 15 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument_arm32.c | 10 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument_debug.c | 20 | ||||
-rw-r--r-- | frida_mode/src/main.c | 8 | ||||
-rw-r--r-- | instrumentation/SanitizerCoverageLTO.so.cc | 12 | ||||
-rw-r--r-- | instrumentation/SanitizerCoveragePCGUARD.so.cc | 14 | ||||
-rw-r--r-- | instrumentation/afl-compiler-rt.o.c | 15 | ||||
-rw-r--r-- | test/test-dlopen.c | 20 | ||||
-rw-r--r-- | utils/libtokencap/libtokencap.so.c | 10 |
15 files changed, 166 insertions, 27 deletions
diff --git a/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.cc b/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.cc index e0273849..ecbfdd1c 100644 --- a/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.cc +++ b/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.cc @@ -99,10 +99,12 @@ extern "C" size_t afl_custom_fuzz(MyMutator *mutator, // return value from afl_c std::string s = ProtoToData(*p); // Copy to a new buffer ( mutated_out ) size_t mutated_size = s.size() <= max_size ? s.size() : max_size; // check if raw data's size is larger than max_size - uint8_t *mutated_out = new uint8_t[mutated_size+1]; - memcpy(mutated_out, s.c_str(), mutated_size); // copy the mutated data + + delete mutator->mutated_out; + mutator->mutated_out = new uint8_t[mutated_size+1]; + memcpy(mutator->mutated_out, s.c_str(), mutated_size); // copy the mutated data // Assign the mutated data and return mutated_size - *out_buf = mutated_out; + *out_buf = mutator->mutated_out; return mutated_size; } diff --git a/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.h b/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.h index ebd3ca65..0f5484ca 100644 --- a/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.h +++ b/custom_mutators/libprotobuf-mutator-example/lpm_aflpp_custom_mutator_input.h @@ -2,4 +2,6 @@ #include "test.pb.h" class MyMutator : public protobuf_mutator::Mutator { +public: + uint8_t *mutated_out = nullptr; }; diff --git a/docs/Changelog.md b/docs/Changelog.md index 103f9f63..be5cac43 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,9 +9,14 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to <afl-users+subscribe@googlegroups.com>. ### Version ++3.15a (dev) - - documentation restructuring, made possible by Google Season of Docs :) + - documentation restructuring, made possible by Google Season of Docs - new binary-only fuzzing mode: coresight_mode for aarch64 CPUs :) thanks to RICSecLab submitting! + - if instrumented libaries are dlopen()'ed after the forkserver you + will now see crashes. before you would have colliding coverage. + we changed this to force fixing a broken setup rather then allowing + ineffective fuzzing. + See docs/best_practices.md how to fix such setups. - afl-fuzz: - cmplog binaries will need to be recompiled for this version (it is better!) diff --git a/docs/best_practices.md b/docs/best_practices.md index e6b252f6..96c6e3c2 100644 --- a/docs/best_practices.md +++ b/docs/best_practices.md @@ -5,6 +5,7 @@ ### Targets * [Fuzzing a target with source code available](#fuzzing-a-target-with-source-code-available) +* [Fuzzing a target with dlopen() instrumented libraries](#fuzzing-a-target-with-dlopen-instrumented-libraries) * [Fuzzing a binary-only target](#fuzzing-a-binary-only-target) * [Fuzzing a GUI program](#fuzzing-a-gui-program) * [Fuzzing a network service](#fuzzing-a-network-service) @@ -21,6 +22,26 @@ To learn how to fuzz a target if source code is available, see [fuzzing_in_depth.md](fuzzing_in_depth.md). +### Fuzzing a target with dlopen instrumented libraries + +If a source code based fuzzing target loads instrumented libraries with +dlopen() after the forkserver has been activated and non-colliding coverage +instrumentation is used (PCGUARD (which is the default), or LTO), then this +an issue, because this would enlarge the coverage map, but afl-fuzz doesn't +know about it. + +The solution is to use `AFL_PRELOAD` for all dlopen()'ed libraries to +ensure that all coverage targets are present on startup in the target, +even if accessed only later with dlopen(). + +For PCGUARD instrumentation `abort()` is called if this is detected, for LTO +there will either be no coverage for the instrumented dlopen()'ed libraries or +you will see lots of crashes in the UI. + +Note that this is not an issue if you use the inferiour `afl-gcc-fast`, +`afl-gcc` or`AFL_LLVM_INSTRUMENT=CLASSIC/NGRAM/CTX afl-clang-fast` +instrumentation. + ### Fuzzing a binary-only target For a comprehensive guide, see diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index 52439979..5303fd1a 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -45,6 +45,11 @@ FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/ FRIDA_TRACE:=$(BUILD_DIR)afl-frida-trace.so FRIDA_TRACE_EMBEDDED:=$(BUILD_DIR)afl-frida-trace-embedded +TARGET_CC?=$(CC) +TARGET_CXX?=$(CXX) +HOST_CC?=$(CC) +HOST_CXX?=$(CXX) + ifndef ARCH ARCH=$(shell uname -m) @@ -99,11 +104,16 @@ ifneq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" "" endif endif +ifeq "$(ARCH)" "armhf" + TARGET_CC:=arm-linux-gnueabihf-gcc + TARGET_CXX:=arm-linux-gnueabihf-g++ +endif + ifndef OS $(error "Operating system unsupported") endif -GUM_DEVKIT_VERSION=15.1.11 +GUM_DEVKIT_VERSION=15.1.13 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)" @@ -188,7 +198,7 @@ $(GUM_DEVIT_HEADER): $(GUM_DEVKIT_TARBALL) ############################## AFL ############################################# $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC) - $(CC) \ + $(TARGET_CC) \ $(CFLAGS) \ $(AFL_CFLAGS) \ -I $(ROOT) \ @@ -197,7 +207,7 @@ $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC) -c $< $(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC) - $(CC) \ + $(TARGET_CC) \ $(CFLAGS) \ $(AFL_CFLAGS) \ -I $(ROOT) \ @@ -208,13 +218,13 @@ $(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC) ############################### JS ############################################# $(BIN2C): $(BIN2C_SRC) - $(CC) -D_GNU_SOURCE -o $@ $< + $(HOST_CC) -D_GNU_SOURCE -o $@ $< $(JS_SRC): $(JS) $(BIN2C)| $(BUILD_DIR) cd $(JS_DIR) && $(BIN2C) api_js $(JS) $@ $(JS_OBJ): $(JS_SRC) GNUmakefile - $(CC) \ + $(TARGET_CC) \ $(CFLAGS) \ -I $(ROOT)include \ -I $(FRIDA_BUILD_DIR) \ @@ -226,7 +236,7 @@ $(JS_OBJ): $(JS_SRC) GNUmakefile define BUILD_SOURCE $(2): $(1) $(INCLUDES) GNUmakefile | $(OBJ_DIR) - $(CC) \ + $(TARGET_CC) \ $(CFLAGS) \ -I $(ROOT)include \ -I $(FRIDA_BUILD_DIR) \ @@ -240,7 +250,7 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $( ######################## AFL-FRIDA-TRACE ####################################### $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) $(AFL_PERFORMANCE_OBJ) GNUmakefile | $(BUILD_DIR) - $(CXX) \ + $(TARGET_CXX) \ $(OBJS) \ $(JS_OBJ) \ $(GUM_DEVIT_LIBRARY) \ @@ -255,10 +265,10 @@ $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL ############################# HOOK ############################################# $(AFLPP_FRIDA_DRIVER_HOOK_OBJ): $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(GUM_DEVIT_HEADER) | $(BUILD_DIR) - $(CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@ + $(TARGET_CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@ $(AFLPP_QEMU_DRIVER_HOOK_OBJ): $(AFLPP_QEMU_DRIVER_HOOK_SRC) | $(BUILD_DIR) - $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + $(TARGET_CC) $(CFLAGS) $(LDFLAGS) $< -o $@ hook: $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h index cac5ee93..a5d52616 100644 --- a/frida_mode/include/instrument.h +++ b/frida_mode/include/instrument.h @@ -36,7 +36,8 @@ void instrument_coverage_optimize(const cs_insn * instr, void instrument_debug_config(void); void instrument_debug_init(void); void instrument_debug_start(uint64_t address, GumStalkerOutput *output); -void instrument_debug_instruction(uint64_t address, uint16_t size); +void instrument_debug_instruction(uint64_t address, uint16_t size, + GumStalkerOutput *output); void instrument_debug_end(GumStalkerOutput *output); void instrument_flush(GumStalkerOutput *output); gpointer instrument_cur(GumStalkerOutput *output); diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index 414dc84c..8ee21f5b 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -193,7 +193,20 @@ static void instrument_basic_block(GumStalkerIterator *iterator, instrument_debug_start(instr->address, output); instrument_coverage_start(instr->address); +#if defined(__arm__) + if (output->encoding == GUM_INSTRUCTION_SPECIAL) { + + prefetch_write(GSIZE_TO_POINTER(instr->address + 1)); + + } else { + + prefetch_write(GSIZE_TO_POINTER(instr->address)); + + } + +#else prefetch_write(GSIZE_TO_POINTER(instr->address)); +#endif if (likely(!excluded)) { @@ -213,7 +226,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator, } - instrument_debug_instruction(instr->address, instr->size); + instrument_debug_instruction(instr->address, instr->size, output); if (likely(!excluded)) { diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c index fa8b0bd2..16e8eaab 100644 --- a/frida_mode/src/instrument/instrument_arm32.c +++ b/frida_mode/src/instrument/instrument_arm32.c @@ -28,7 +28,15 @@ void instrument_coverage_optimize_init(void) { void instrument_flush(GumStalkerOutput *output) { - gum_arm_writer_flush(output->writer.arm); + if (output->encoding == GUM_INSTRUCTION_SPECIAL) { + + gum_thumb_writer_flush(output->writer.thumb); + + } else { + + gum_arm_writer_flush(output->writer.arm); + + } } diff --git a/frida_mode/src/instrument/instrument_debug.c b/frida_mode/src/instrument/instrument_debug.c index a175b585..9c95857f 100644 --- a/frida_mode/src/instrument/instrument_debug.c +++ b/frida_mode/src/instrument/instrument_debug.c @@ -32,18 +32,27 @@ static void instrument_debug(char *format, ...) { } -static void instrument_disasm(guint8 *start, guint8 *end) { +static void instrument_disasm(guint8 *start, guint8 *end, + GumStalkerOutput *output) { csh capstone; cs_err err; + cs_mode mode; uint16_t size; cs_insn *insn; size_t count = 0; size_t i; uint16_t len; + mode = GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN; + +#if defined(__arm__) + if (output->encoding == GUM_INSTRUCTION_SPECIAL) { mode |= CS_MODE_THUMB; } +#endif + err = cs_open(GUM_DEFAULT_CS_ARCH, - GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN, &capstone); + CS_MODE_THUMB | GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN, + &capstone); g_assert(err == CS_ERR_OK); size = GPOINTER_TO_SIZE(end) - GPOINTER_TO_SIZE(start); @@ -121,11 +130,12 @@ void instrument_debug_start(uint64_t address, GumStalkerOutput *output) { } -void instrument_debug_instruction(uint64_t address, uint16_t size) { +void instrument_debug_instruction(uint64_t address, uint16_t size, + GumStalkerOutput *output) { if (likely(debugging_fd < 0)) { return; } uint8_t *start = (uint8_t *)GSIZE_TO_POINTER(address); - instrument_disasm(start, start + size); + instrument_disasm(start, start + size, output); } @@ -136,7 +146,7 @@ void instrument_debug_end(GumStalkerOutput *output) { instrument_debug("\nGenerated block %p-%p\n", instrument_gen_start, instrument_gen_end); - instrument_disasm(instrument_gen_start, instrument_gen_end); + instrument_disasm(instrument_gen_start, instrument_gen_end, output); } diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c index 913e3a46..1be63bc4 100644 --- a/frida_mode/src/main.c +++ b/frida_mode/src/main.c @@ -219,6 +219,8 @@ __attribute__((visibility("default"))) void afl_frida_start(void) { static int on_main(int argc, char **argv, char **envp) { + int ret; + on_main_os(argc, argv, envp); intercept_unhook_self(); @@ -227,14 +229,16 @@ static int on_main(int argc, char **argv, char **envp) { if (js_main_hook != NULL) { - return js_main_hook(argc, argv, envp); + ret = js_main_hook(argc, argv, envp); } else { - return main_fn(argc, argv, envp); + ret = main_fn(argc, argv, envp); } + return ret; + } #if defined(EMBEDDED) diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index bff85a0a..8d7f0c80 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -1289,6 +1289,18 @@ void ModuleSanitizerCoverage::instrumentFunction( if (!Callee) continue; if (callInst->getCallingConv() != llvm::CallingConv::C) continue; StringRef FuncName = Callee->getName(); + if (!FuncName.compare(StringRef("dlopen")) || + !FuncName.compare(StringRef("_dlopen"))) { + + fprintf(stderr, + "WARNING: dlopen() detected. To have coverage for a library " + "that your target dlopen()'s this must either happen before " + "__AFL_INIT() or you must use AFL_PRELOAD to preload all " + "dlopen()'ed libraries!\n"); + continue; + + } + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; Value *val = ConstantInt::get(Int32Ty, ++afl_global_id); diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 3574b0e4..d5746cc7 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -851,6 +851,18 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, if (!Callee) continue; if (callInst->getCallingConv() != llvm::CallingConv::C) continue; StringRef FuncName = Callee->getName(); + if (!FuncName.compare(StringRef("dlopen")) || + !FuncName.compare(StringRef("_dlopen"))) { + + fprintf(stderr, + "WARNING: dlopen() detected. To have coverage for a library " + "that your target dlopen()'s this must either happen before " + "__AFL_INIT() or you must use AFL_PRELOAD to preload all " + "dlopen()'ed libraries!\n"); + continue; + + } + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; cnt_cov++; @@ -877,7 +889,7 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, if (tt) { cnt_sel++; - cnt_sel_inc += tt->getElementCount().getFixedValue(); + cnt_sel_inc += tt->getElementCount().getKnownMinValue(); } diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index cc73e5ec..20f325f3 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1417,6 +1417,18 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (start == stop || *start) return; + // If a dlopen of an instrumented library happens after the forkserver then + // we have a problem as we cannot increase the coverage map anymore. + if (__afl_already_initialized_forkserver) { + + fprintf(stderr, + "[-] FATAL: forkserver is already up, but an instrumented dlopen() " + "library loaded afterwards. You must AFL_PRELOAD such libraries to " + "be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n"); + abort(); + + } + x = getenv("AFL_INST_RATIO"); if (x) inst_ratio = (u32)atoi(x); @@ -1429,6 +1441,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { /* instrumented code is loaded *after* our forkserver is up. this is a problem. We cannot prevent collisions then :( */ + /* if (__afl_already_initialized_forkserver && __afl_final_loc + 1 + stop - start > __afl_map_size) { @@ -1461,6 +1474,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + */ + /* Make sure that the first element in the range is always set - we use that to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ diff --git a/test/test-dlopen.c b/test/test-dlopen.c index d08d9092..b81bab13 100644 --- a/test/test-dlopen.c +++ b/test/test-dlopen.c @@ -5,7 +5,13 @@ int main(int argc, char **argv) { - if (!getenv("TEST_DLOPEN_TARGET")) return 1; + if (!getenv("TEST_DLOPEN_TARGET")) { + + fprintf(stderr, "Error: TEST_DLOPEN_TARGET not set!\n"); + return 1; + + } + void *lib = dlopen(getenv("TEST_DLOPEN_TARGET"), RTLD_LAZY); if (!lib) { @@ -15,8 +21,18 @@ int main(int argc, char **argv) { } int (*func)(int, char **) = dlsym(lib, "main_exported"); - if (!func) return 3; + if (!func) { + + fprintf(stderr, "Error: main_exported not found!\n"); + return 3; + + } + + // must use deferred forkserver as otherwise afl++ instrumentation aborts + // because all dlopen() of instrumented libs must be before the forkserver + __AFL_INIT(); + fprintf(stderr, "Running main_exported\n"); return func(argc, argv); } diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c index 3629e804..2b1e3903 100644 --- a/utils/libtokencap/libtokencap.so.c +++ b/utils/libtokencap/libtokencap.so.c @@ -33,6 +33,8 @@ #include "../types.h" #include "../config.h" +#include "debug.h" + #if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \ !defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__ && \ !defined(__HAIKU__) && !defined(__sun) @@ -169,7 +171,7 @@ static void __tokencap_load_mappings(void) { int mib[] = {CTL_VM, VM_PROC, VM_PROC_MAP, __tokencap_pid, sizeof(struct kinfo_vmentry)}; #endif - char * buf, *low, *high; + char *buf, *low, *high; size_t miblen = sizeof(mib) / sizeof(mib[0]); size_t len; @@ -343,6 +345,12 @@ static void __tokencap_dump(const u8 *ptr, size_t len, u8 is_text) { wrt_ok &= (pos == write(__tokencap_out_file, buf, pos)); wrt_ok &= (2 == write(__tokencap_out_file, "\"\n", 2)); + if (!wrt_ok) { + + DEBUGF("%s", "writing to the token file failed\n"); + + } + } /* Replacements for strcmp(), memcmp(), and so on. Note that these will be used |