diff --git a/Lab7/Makefile b/Lab7/Makefile new file mode 100644 index 0000000..83ef961 --- /dev/null +++ b/Lab7/Makefile @@ -0,0 +1,11 @@ +all: + ./build.sh + AFL_DEMO=1 ./AFLplusplus/afl-clang-fast -O0 -w program.c -o program + mkdir -p input + echo -ne "\x00" > input/seed0 + +run: + AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFLplusplus/afl-fuzz -i input -o output -- ./program + +clean: + rm -rf AFLplusplus input output program diff --git a/Lab7/README.md b/Lab7/README.md new file mode 100644 index 0000000..818b8a5 --- /dev/null +++ b/Lab7/README.md @@ -0,0 +1,77 @@ +# Lab7 + +## Introduction + +In this lab, you will write a LLVM pass in `llvm-pass/afl-demo-pass.so.cc` and `llvm-pass/afl-demo-rt.o.c`, and enable AFL++ to detect command injection vulnerabilities. + +## Environment (Important.) + +1. x86_64 machine +2. Ubuntu 22.04 +3. llvm-14 + +## Requirement + +**(100%)** In this lab, you will write a LLVM pass in `llvm-pass/afl-demo-pass.so.cc` & `llvm-pass/afl-demo-rt.o.c` and satisfy the following requirements. +1. When running afl++ to fuzz `program`, `program` can't crash by `system("echo AAA");` at line 34 in `program.c`. It's not command injection, and you will see the error message below when you try to fuzz: + ``` + [-] PROGRAM ABORT : We need at least one valid input seed that does not crash! + ``` + Of course, you can not just detect this line to avoid it. We will modify this command and add additional system functions (which won't cause command injection) in `program.c` during testing with your fuzzer. +2. When running afl++ to fuzz `program`, the fuzzer needs to find the command injection `system(cmd)` at line 18 in `program.c`. +3. Write a report in `{student_ID}.pdf` to explain your work. + - Your report must include the following sections + - Your name and student ID, + - Explanation of your work + - Copy the crash fuzzer report and crash input (use `xxd`) in `{student_ID}.pdf`. + +**Template** + + Name: + student_ID: + + ``` + Explanation of your work + ``` + + Crash fuzzer report + ``` + + american fuzzy lop ++4.01c {default} (./program) [fast] + ┌─ process timing ────────────────────────────────────┬─ overall results ────┐ + │ run time : 0 days, 0 hrs, 0 min, 18 sec │ cycles done : 9 │ + │ last new find : 0 days, 0 hrs, 0 min, 8 sec │ corpus count : 7 │ + │last saved crash : 0 days, 0 hrs, 0 min, 3 sec │saved crashes : 1 │ + │ last saved hang : none seen yet │ saved hangs : 0 │ + ├─ cycle progress ─────────────────────┬─ map coverage┴──────────────────────┤ + │ now processing : 5.3 (71.4%) │ map density : 30.77% / 76.92% │ + │ runs timed out : 0 (0.00%) │ count coverage : 39.40 bits/tuple │ + ├─ stage progress ─────────────────────┼─ findings in depth ─────────────────┤ + │ now trying : havoc │ favored items : 7 (100.00%) │ + │ stage execs : 312/1175 (26.55%) │ new edges on : 7 (100.00%) │ + │ total execs : 97.2k │ total crashes : 1 (1 saved) │ + │ exec speed : 5329/sec │ total tmouts : 1 (0 saved) │ + ├─ fuzzing strategy yields ────────────┴─────────────┬─ item geometry ───────┤ + │ bit flips : disabled (default, enable with -D) │ levels : 6 │ + │ byte flips : disabled (default, enable with -D) │ pending : 0 │ + │ arithmetics : disabled (default, enable with -D) │ pend fav : 0 │ + │ known ints : disabled (default, enable with -D) │ own finds : 6 │ + │ dictionary : n/a │ imported : 0 │ + │havoc/splice : 4/38.3k, 3/58.5k │ stability : 100.00% │ + │py/custom/rq : unused, unused, unused, unused ├───────────────────────┘ + │ trim/eff : 0.00%/7, disabled │ [cpu000: 12%] + └────────────────────────────────────────────────────┘ + + ``` + Crash Input: + ``` + $ xxd output/default/crashes/id\:000000\,sig\:06\,src\:000006\,time\:15137\,execs\:81803\,op\:havoc\,rep\:2 + 00000000: 4655 5a5a 2146 ec40 FUZZ!F.@ + ``` + +## Submission + +1. Lab7 will not have github ci, so the grade of Lab7 will be determined with your report `{studemt_ID}.pdf` +2. Submit the report `{student_ID}.pdf` to explain your work. +3. You must submit these two files `llvm-pass/afl-demo-pass.so.cc` and `llvm-pass/afl-demo-rt.o.c`. +4. You need to commit and push the corresponding changes to your repository, which contains the code that satisfies the aforementioned requirements. diff --git a/Lab7/build.sh b/Lab7/build.sh new file mode 100755 index 0000000..06808bc --- /dev/null +++ b/Lab7/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash -ex + +if [ ! -d "AFLplusplus" ]; then + git clone -b 4.01c --depth 1 https://github.com/AFLplusplus/AFLplusplus.git + git -C AFLplusplus apply ../integration.diff +fi + +cp llvm-pass/* AFLplusplus/instrumentation +cd AFLplusplus +make LLVM_CONFIG=llvm-config-14 \ No newline at end of file diff --git a/Lab7/integration.diff b/Lab7/integration.diff new file mode 100644 index 0000000..d0f486b --- /dev/null +++ b/Lab7/integration.diff @@ -0,0 +1,108 @@ +diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm +index 685964b7..9f309c08 100644 +--- a/GNUmakefile.llvm ++++ b/GNUmakefile.llvm +@@ -308,7 +308,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 ./afl-demo-pass.so ./afl-demo-rt.o + + # If prerequisites are not given, warn, do not build anything, and exit with code 0 + ifeq "$(LLVMVER)" "" +@@ -436,6 +436,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 + ++afl-demo-pass.so: instrumentation/afl-demo-pass.so.cc instrumentation/afl-llvm-common.o | test_deps ++ $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) ++ + .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 +@@ -453,6 +456,9 @@ document: + @printf "[*] Building 64-bit variant of the runtime (-m64)... " + @$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + ++./afl-demo-rt.o: instrumentation/afl-demo-rt.o.c ++ $(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@ ++ + .PHONY: test_build + test_build: $(PROGS) + @echo "[*] Testing the CC wrapper and instrumentation output..." +@@ -475,6 +481,7 @@ install: all + @if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi + @rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt*.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt*.o + @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); fi ++ @if [ -f ./afl-demo-rt.o ]; then set -e; install -m 755 ./afl-demo-rt.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi + @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); fi +diff --git a/include/envs.h b/include/envs.h +index 25b792fa..bbe9a99e 100644 +--- a/include/envs.h ++++ b/include/envs.h +@@ -226,6 +226,7 @@ static char *afl_environment_variables[] = { + "AFL_USE_QASAN", + "AFL_PRINT_FILENAMES", + "AFL_PIZZA_MODE", ++ "AFL_DEMO", + NULL + + }; +diff --git a/src/afl-cc.c b/src/afl-cc.c +index 2667ae28..7da0b89c 100644 +--- a/src/afl-cc.c ++++ b/src/afl-cc.c +@@ -57,6 +57,7 @@ static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; + static u8 debug; + static u8 cwd[4096]; + static u8 cmplog_mode; ++static u8 demo_mode; + u8 use_stdin; /* dummy */ + static int passthrough; + // static u8 *march_opt = CFLAGS_OPT; +@@ -699,6 +700,23 @@ static void edit_params(u32 argc, char **argv, char **envp) { + + } + ++ ++ if (demo_mode) { ++ ++#if LLVM_MAJOR >= 11 ++ cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; ++ cc_params[cc_par_cnt++] = alloc_printf( ++ "-fpass-plugin=%s/afl-demo-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/afl-demo-pass.so", obj_path); ++#endif ++ ++ } ++ + // cc_params[cc_par_cnt++] = "-Qunused-arguments"; + + if (lto_mode && argc > 1) { +@@ -1100,6 +1118,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); ++ if (demo_mode) ++ cc_params[cc_par_cnt++] = ++ alloc_printf("%s/afl-demo-rt.o", obj_path); + break; + + case 32: +@@ -2153,6 +2174,8 @@ int main(int argc, char **argv, char **envp) { + if (!be_quiet && cmplog_mode) + printf("CmpLog mode by \n"); + ++ demo_mode = !!getenv("AFL_DEMO"); ++ + #if !defined(__ANDROID__) && !defined(ANDROID) + ptr = find_object("afl-compiler-rt.o", argv[0]); + diff --git a/Lab7/llvm-pass/afl-demo-pass.so.cc b/Lab7/llvm-pass/afl-demo-pass.so.cc new file mode 100644 index 0000000..4e40c06 --- /dev/null +++ b/Lab7/llvm-pass/afl-demo-pass.so.cc @@ -0,0 +1,137 @@ +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#include "llvm/Pass.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/IR/PassManager.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Constants.h" + +#include "llvm/Passes/OptimizationLevel.h" + +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class AFLDEMOPass : public PassInfoMixin { + + public: + AFLDEMOPass() { + + } + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); + llvm::StringRef GetCallInsFunctionName(CallInst *call); + + protected: + +}; + +} // namespace + +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return { + + LLVM_PLUGIN_API_VERSION, "AFLDEMOPass", "v0.1", [](PassBuilder &PB) { + + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(AFLDEMOPass()); + + }); + + }}; + +} + +PreservedAnalyses AFLDEMOPass::run(Module &M, ModuleAnalysisManager &MAM) { + + LLVMContext & C = M.getContext(); + Type * VoidTy = Type::getVoidTy(C); + FunctionCallee demo_crash = M.getOrInsertFunction("__demo_crash", VoidTy); + + for (auto &F : M) { + + llvm::StringRef fn = F.getName(); + + if (fn.equals("_start") || fn.startswith("__libc_csu") || + fn.startswith("__afl_") || fn.startswith("__asan") || + fn.startswith("asan.") || fn.startswith("llvm.")) + continue; + + for (auto &BB : F) { + + for (auto &I : BB) { + + if (CallInst *call = dyn_cast(&I)) { + + if (GetCallInsFunctionName(call).equals("system")) { + + IRBuilder<> IRB(&I); + IRB.CreateCall(demo_crash)->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + } + + } + + } + + return PreservedAnalyses::all(); + +} + +llvm::StringRef AFLDEMOPass::GetCallInsFunctionName(CallInst *call) { + + if (Function *func = call->getCalledFunction()) { + + return func->getName(); + + } else { + + // Indirect call + return dyn_cast(call->getCalledOperand()->stripPointerCasts()) + ->getName(); + + } + +} + diff --git a/Lab7/llvm-pass/afl-demo-rt.o.c b/Lab7/llvm-pass/afl-demo-rt.o.c new file mode 100644 index 0000000..7bc7a22 --- /dev/null +++ b/Lab7/llvm-pass/afl-demo-rt.o.c @@ -0,0 +1,43 @@ +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "cmplog.h" +#include "llvm-alternative-coverage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifndef __HAIKU__ + #include +#endif +#ifndef USEMMAP + #include +#endif +#include +#include + +#if !__GNUC__ + #include "llvm/Config/llvm-config.h" +#endif + +#ifdef __linux__ + #include "snapshot-inl.h" +#endif + +void __demo_crash() { + + fprintf(stderr, "system found!\n"); + abort(); + +} \ No newline at end of file diff --git a/Lab7/program.c b/Lab7/program.c new file mode 100644 index 0000000..53445dc --- /dev/null +++ b/Lab7/program.c @@ -0,0 +1,39 @@ +#include +#include +#include + +void process_input(const char *data) { + if (strlen(data) < 5) return; + + if (strlen(data) > 0x8) return; + + if (data[0] == 'F') { + if (data[1] == 'U') { + if (data[2] == 'Z') { + if (data[3] == 'Z') { + if (data[4] == '!') { + // Simulate a bug/crash + char cmd[0x20]; + snprintf(cmd, 0x20 - 1, "echo %s", data); + system(cmd); + } + } + } + } + } +} + +int main(int argc, char **argv) { + char buf[1024]; + int bytes_read = read(0, buf, sizeof(buf) - 1); + if (bytes_read < 0) { + perror("read"); + return 1; + } + + system("echo AAA"); + + buf[bytes_read] = '\0'; + process_input(buf); + return 0; +} \ No newline at end of file