diff options
author | van Hauser <vh@thc.org> | 2023-12-30 10:51:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-30 10:51:47 +0100 |
commit | 2260a065f41d055d7e1515c620c18772ae7915d6 (patch) | |
tree | 19553169a33d1506c3a6bac675caa60adf24bff3 | |
parent | c39596c8bf44c9fe04180d9cf298fd2db36637ac (diff) | |
parent | 1eb54c4c3eb4ab4bc12f7f1f80f5ece15b238ef0 (diff) | |
download | afl++-2260a065f41d055d7e1515c620c18772ae7915d6.tar.gz |
Merge pull request #1944 from AFLplusplus/inject
Inject fuzzing
-rw-r--r-- | GNUmakefile | 5 | ||||
-rw-r--r-- | GNUmakefile.llvm | 5 | ||||
-rw-r--r-- | docs/env_variables.md | 13 | ||||
-rw-r--r-- | include/envs.h | 5 | ||||
-rw-r--r-- | injections.dic | 7 | ||||
-rw-r--r-- | instrumentation/README.injections.md | 48 | ||||
-rw-r--r-- | instrumentation/afl-compiler-rt.o.c | 49 | ||||
-rw-r--r-- | src/afl-cc.c | 24 | ||||
-rw-r--r-- | src/afl-fuzz.c | 28 |
9 files changed, 181 insertions, 3 deletions
diff --git a/GNUmakefile b/GNUmakefile index 364cdde1..b67f9c15 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -782,7 +782,7 @@ install: all $(MANPAGES) @rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh @rm -f $${DESTDIR}$(BIN_PATH)/afl-as @rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o - @for i in afl-llvm-dict2file.so afl-llvm-lto-instrumentlist.so afl-llvm-pass.so cmplog-instructions-pass.so cmplog-routines-pass.so cmplog-switches-pass.so compare-transform-pass.so libcompcov.so libdislocator.so libnyx.so libqasan.so libtokencap.so SanitizerCoverageLTO.so SanitizerCoveragePCGUARD.so split-compares-pass.so split-switches-pass.so; do echo rm -fv $${DESTDIR}$(HELPER_PATH)/$${i}; done + @for i in afl-llvm-dict2file.so afl-llvm-lto-instrumentlist.so afl-llvm-pass.so cmplog-instructions-pass.so cmplog-routines-pass.so cmplog-switches-pass.so compare-transform-pass.so libcompcov.so libdislocator.so libnyx.so libqasan.so libtokencap.so SanitizerCoverageLTO.so SanitizerCoveragePCGUARD.so split-compares-pass.so split-switches-pass.so injection-pass.so; do echo rm -fv $${DESTDIR}$(HELPER_PATH)/$${i}; done install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) @if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi @if [ -f utils/plot_ui/afl-plot-ui ]; then install -m 755 utils/plot_ui/afl-plot-ui $${DESTDIR}$(BIN_PATH); fi @@ -813,11 +813,12 @@ endif install -m 644 docs/*.md $${DESTDIR}$(DOC_PATH) cp -r testcases/ $${DESTDIR}$(MISC_PATH) cp -r dictionaries/ $${DESTDIR}$(MISC_PATH) + cp injections.dic $${DESTDIR}$(MISC_PATH) .PHONY: uninstall uninstall: -cd $${DESTDIR}$(BIN_PATH) && rm -f $(PROGS) $(SH_PROGS) afl-cs-proxy afl-qemu-trace afl-plot-ui afl-fuzz-document afl-network-client afl-network-server afl-g* afl-plot.sh afl-as afl-ld-lto afl-c* afl-lto* - -cd $${DESTDIR}$(HELPER_PATH) && rm -f afl-g*.*o afl-llvm-*.*o afl-compiler-*.*o libdislocator.so libtokencap.so libcompcov.so libqasan.so afl-frida-trace.so libnyx.so socketfuzz*.so argvfuzz*.so libAFLDriver.a libAFLQemuDriver.a as afl-as SanitizerCoverage*.so compare-transform-pass.so cmplog-*-pass.so split-*-pass.so dynamic_list.txt + -cd $${DESTDIR}$(HELPER_PATH) && rm -f afl-g*.*o afl-llvm-*.*o afl-compiler-*.*o libdislocator.so libtokencap.so libcompcov.so libqasan.so afl-frida-trace.so libnyx.so socketfuzz*.so argvfuzz*.so libAFLDriver.a libAFLQemuDriver.a as afl-as SanitizerCoverage*.so compare-transform-pass.so cmplog-*-pass.so split-*-pass.so dynamic_list.txt injections.dic -rm -rf $${DESTDIR}$(MISC_PATH)/testcases $${DESTDIR}$(MISC_PATH)/dictionaries -sh -c "ls docs/*.md | sed 's|^docs/|$${DESTDIR}$(DOC_PATH)/|' | xargs rm -f" -cd $${DESTDIR}$(MAN_PATH) && rm -f $(MANPAGES) diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 0845ae3a..c704d772 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -341,7 +341,7 @@ ifeq "$(TEST_MMAP)" "1" endif PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o -PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./SanitizerCoverageLTO.so +PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./SanitizerCoverageLTO.so ./injection-pass.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -469,6 +469,9 @@ endif afl-llvm-dict2file.so: instrumentation/afl-llvm-dict2file.so.cc instrumentation/afl-llvm-common.o | test_deps $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./injection-pass.so: instrumentation/injection-pass.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + .PHONY: document document: $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt.o diff --git a/docs/env_variables.md b/docs/env_variables.md index a7636511..a972b6da 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -196,6 +196,19 @@ in the specified file. For more information, see [instrumentation/README.instrument_list.md](../instrumentation/README.instrument_list.md). +#### INJECTIONS + +This feature is able to find simple injection vulnerabilities in insecure +calls to mysql/mariadb/nosql/postgresql/ldap and XSS in libxml2. + + - Setting `AFL_LLVM_INJECTIONS_ALL` will enable all injection hooking + + - Setting `AFL_LLVM_INJECTIONS_SQL` will enable SQL injection hooking + + - Setting `AFL_LLVM_INJECTIONS_LDAP` will enable LDAP injection hooking + + - Setting `AFL_LLVM_INJECTIONS_XSS` will enable XSS injection hooking + #### LAF-INTEL This great feature will split compares into series of single byte comparisons to diff --git a/include/envs.h b/include/envs.h index 560092d9..aa5c658e 100644 --- a/include/envs.h +++ b/include/envs.h @@ -151,6 +151,11 @@ static char *afl_environment_variables[] = { "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", "AFL_LLVM_SKIPSINGLEBLOCK", + // Marker: ADD_TO_INJECTIONS + "AFL_LLVM_INJECTIONS_ALL", + "AFL_LLVM_INJECTIONS_SQL", + "AFL_LLVM_INJECTIONS_LDAP", + "AFL_LLVM_INJECTIONS_XSS", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", diff --git a/injections.dic b/injections.dic new file mode 100644 index 00000000..4063cd17 --- /dev/null +++ b/injections.dic @@ -0,0 +1,7 @@ +"1'\" OR \"1\"=\"1" +"1\"' OR '1'='1" +"'\"><FUZZ" +"*)(FUZZ=*))(|" +"\";FUZZ;\"" +"';FUZZ;'" +"$(FUZZ)" diff --git a/instrumentation/README.injections.md b/instrumentation/README.injections.md new file mode 100644 index 00000000..16cc3713 --- /dev/null +++ b/instrumentation/README.injections.md @@ -0,0 +1,48 @@ +# Injection fuzzing + +Coverage guided fuzzing so far is only able to detect crashes, so usually +memory corruption issues, or - if implemented by hand in the harness - +invariants. + +This is a proof-of-concept implementation to additionally hunt for injection +vulnerabilities. +It works by instrumenting calls to specific functions and parsing the +query parameter for a specific unescaped dictionary string, and if detected, +crashes the target. + +This has a very low false positive rate. +But obviously this can only find injection vulnerailities that are suspectible +to this specific (but most common) issue. Hence in a rare kind of injection +vulnerability this won't find the bug - and be a false negative. +But this can be tweaked by the user - see the HOW TO MODIFY section below. + +## How to use + +Set one or more of the following environment variables for **compiling** +the target and - *this is important* - when **fuzzing** the target: + + - `AFL_LLVM_INJECTIONS_SQL` + - `AFL_LLVM_INJECTIONS_LDAP` + - `AFL_LLVM_INJECTIONS_XSS` + +Alternatively you can set `AFL_LLVM_INJECTIONS_ALL` to enable all. + +## How to modify + +If you want to add more fuctions to check for e.g. SQL injections: +Add these to `instrumentation/injection-pass.cc` and recompile. + +If you want to test for more injection inputs: +Add the dictionary tokens to `src/afl-fuzz.c` and the check for them to +`instrumentation/afl-compiler-rt.o.c`. + +If you want to add new injection targets: +You will have to edit all three files. + +Just search for: +``` +// Marker: ADD_TO_INJECTIONS +``` +in the files to see where this needs to be added. + +**NOTE:** pull requests to improve this feature are highly welcome :-) diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index def59b6b..39a762b6 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -92,6 +92,8 @@ extern ssize_t _kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize); #endif // HAIKU +char *strcasestr(const char *haystack, const char *needle); + static u8 __afl_area_initial[MAP_INITIAL_SIZE]; static u8 *__afl_area_ptr_dummy = __afl_area_initial; static u8 *__afl_area_ptr_backup = __afl_area_initial; @@ -2670,5 +2672,52 @@ void __afl_set_persistent_mode(u8 mode) { } +// Marker: ADD_TO_INJECTIONS + +void __afl_injection_sql(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "'\"\"'"))) { + + fprintf(stderr, "ALERT: Detected SQL injection in query: %s\n", buf); + abort(); + + } + + } + +} + +void __afl_injection_ldap(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "*)(1=*))(|"))) { + + fprintf(stderr, "ALERT: Detected LDAP injection in query: %s\n", buf); + abort(); + + } + + } + +} + +void __afl_injection_xss(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "1\"><\""))) { + + fprintf(stderr, "ALERT: Detected XSS injection in content: %s\n", buf); + abort(); + + } + + } + +} + #undef write_error diff --git a/src/afl-cc.c b/src/afl-cc.c index 22cce2cd..54c733c9 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1139,6 +1139,26 @@ static void edit_params(u32 argc, char **argv, char **envp) { } + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_SQL") || + getenv("AFL_LLVM_INJECTIONS_LDAP") || + getenv("AFL_LLVM_INJECTIONS_XSS")) { + +#if LLVM_MAJOR >= 11 + #if LLVM_MAJOR < 16 + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%s/injection-pass.so", obj_path); +#else + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = alloc_printf("%s/injection-pass.so", obj_path); +#endif + + } + // cc_params[cc_par_cnt++] = "-Qunused-arguments"; } @@ -2275,6 +2295,10 @@ int main(int argc, char **argv, char **envp) { "comparisons\n" " AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the " "dictionary\n" + " AFL_LLVM_INJECTIONS_ALL: enables all injections hooking\n" + " AFL_LLVM_INJECTIONS_SQL: enables SQL injections hooking\n" + " AFL_LLVM_INJECTIONS_LDAP: enables LDAP injections hooking\n" + " AFL_LLVM_INJECTIONS_XSS: enables XSS injections hooking\n" " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index dd990e71..17949fd7 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1749,6 +1749,34 @@ int main(int argc, char **argv_orig, char **envp) { } + // Marker: ADD_TO_INJECTIONS + if (getenv("AFL_LLVM_INJECTIONS_ALL") || getenv("AFL_LLVM_INJECTIONS_SQL") || + getenv("AFL_LLVM_INJECTIONS_LDAP") || getenv("AFL_LLVM_INJECTIONS_XSS")) { + + OKF("Adding injection tokens to dictionary."); + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_SQL")) { + + add_extra(afl, "'\"\"'", 4); + + } + + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_LDAP")) { + + add_extra(afl, "*)(1=*))(|", 10); + + } + + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_XSS")) { + + add_extra(afl, "1\"><\"", 5); + + } + + } + OKF("Generating fuzz data with a length of min=%u max=%u", afl->min_length, afl->max_length); u32 min_alloc = MAX(64U, afl->min_length); |