afl实验
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
Install afl
-
My linux: CentOS Linux 7
-
Use below commands to install afl:
git clone https://github.com/mirrorer/afl cd afl make sudo make install
-
Use command
ls -l /usr/local/bin/afl*
to check the installed files.•
afl-gcc
andafl-g ++
correspond to thegcc
andg ++
packages, respectively •afl-clang
andafl-clang ++
correspond to clang’s c and c ++ compiler packages, respectively. •afl-fuzz
is the main body of AFL, used to fuzz the target program. •afl-analyze
can analyze use cases. By analyzing a given use case, you can see if you can find meaningful fields in the use case. •afl-qemu-trace
is used in qemu-mode, which is not installed by default. You need to manually execute the compilation script of qemu-mode to compile, which will be described later. •afl-plot
generates a state diagram of the test task •afl-tmin
andafl-cmin
simplify the use cases •afl-whatsup
is used to view the status of fuzz tasks •afl-gotcpu
is used to view the current CPU status •afl-showmap
is used to trace the execution path of a single use case
The cJSON project
-
I select the
cJSON
project to do my test work. This is a ultralight weight JSON parser in ANSI C. I downloaded the source code from github:git clone https://github.com/DaveGamble/cJSON.git
-
Then I use the
cloc
tool to counts, and computes differences of, comment lines, blank lines, and physical lines of source code incJSON
project.sudo yum install cloc cloc .
From this figure, we learn this
cJSON
project has 3594 code lines in two files (except test program).
- I analyze the source code to learn about the feature of functions’ input and output. Here I list several functions in
cJSON.c
:
Function name Input type Output type cJSON_CreateObject void cJSON* cJSON_AddNumberToObject cJSON * const , const char * , const double cJSON * cJSON_Delete cJSON * void cJSON_Print const cJSON * char * cJSON_CreateNumber double cJSON* cJSON_New_Item const internal_hooks * const cJSON* Prepare for fuzzing
Update the test program
The given test program use constant strings as test cases.
For the data generated by AFL to actually test the library, we have to write a test program that will take external input and feed it to the library. This can either be from a file specified on the command line, or directly from stdin.
I update the program to accept input directly from stdin. You could see the program code in Appendix 1.
Update the Makefile and compile
To allow AFL to work effectively, the code needs to be instrumented - so we have to compile it using one of afl-clang-fast, afl-clang, or afl-gcc. Therefore, I change
CC = gcc -std=c89
toCC = /usr/local/bin/afl-gcc -std=c89
in Makefile, then make.Design a test case
To operate correctly, the fuzzer requires one or more starting file that contains a good example of the input data normally expected by the targeted application. There are two basic rules:
-
l Keep the files small. Under 1 kB is ideal.
-
l Use multiple test cases only if they are functionally different from each other.
According the rules above, we design a test case: a string “hello” in ./testcases/test.txt
Start Fuzzing
After compiling this with the instrumenting compiler, running it under afl-fuzz should give (marginally) better results.
The fuzzing process itself is carried out by the afl-fuzz utility. This program requires:
- A read-only directory with initial test cases: testcases
- A separate place to store its findings: out
- A path to the binary to test: ./cJSON_test
The whole program for target binaries that accept input directly from stdin is:
/usr/local/bin/afl-fuzz -i testcases/ -o out ./cJSON_test ./testcases/
The inputs it is sending to the program are actually having an impact on the execution flow, and it can discover inputs that lead to different paths.
Afl status window
- Process timing: How long the Fuzzer has been running, and how long it has been since the closest path was found, crashed, and hung.
- Overall results: An overview of the current state of Fuzzer.
- Cycle progress: we enter the distance of the queue.
- Map coverage: the details of the coverage observed by the instrumentation code in the target binary file.
- Stage progress: File mutation strategy, execution times and execution speed that Fuzzer is currently executing.
- Findings in depth: information about the execution path, anomalies and hangs we found.
- Fuzzing strategy yields: Detailed information about the latest behaviors and results produced by mutation strategies.
- Path geometry: information about the execution path found by Fuzzer.
- CPU load: CPU utilization
When to end?
As the number of cycles continues to increase, The color of the “cycles done” field in the status window will gradually change from magenta to yellow, blue, and green. The color of this field can be used as a reference when to stop the test. . When it turns green, it’s hard to find new ones if you continue to fuzzing, then you can stop afl-fuzz by
Ctrl-C
.Analyze the output
What’s in the output directory ?
There are three subdirectories created within the output directory and updated in real time:
-
queue/ : test cases for every distinctive execution path, plus all the starting files given by the user.
- id:000000 is the test case that I design, the other 77 paths are afl found.
- crashes/id* : unique test cases that cause the tested program to receive a fatal signal (e.g., SIGSEGV, SIGILL, SIGABRT). The entries are grouped by the received signal.
- There are 16 unique crashes.
-
crashes / README.txt: save the command line parameters of the target to execute these crash files
-
hangs/ : unique test cases that cause the tested program to time out. The default time limit before something is classified as a hang is the larger of 1 second and the value of the -t parameter. The value can be fine-tuned by setting AFL_HANG_TMOUT, but this is rarely necessary.
-
fuzzer_stats: running status of afl-fuzz.
- plot_data: used for afl-plot drawing.
Use afl-collect to analyze crashes
afl-collect
basically copies all crash sample files from an afl synchronisation directory (used by multiple afl instances when run in parallel) into a single location providing easy access for further crash analysis.Beyond that
afl-collect
has some more advanced features like invalid crash sample removing (seeafl-vcrash
) as well as generating and executinggdb
scripts that make use of Exploitable. The purpose of these scripts is to automate crash sample classification (see screenshot below) and reduction.afl-collect -j 8 -d crashes.db -e gdb_script ./out ./collection_dir -- ./cJSON_test --target-opts
- sig: 06 Stack Buffer Overflow, which may be caused by the execution of the abort \ assert function or double free.
- sig: 07 Possible Stack Corruption , it may be that some memory locations at stack are accessed unintentionally due to wrong coding leading to change in values at those memory locations.
- sig: 11 Dest Av, it may be that the process references invalid memory due to a buffer overflow.
Conclusion
- Installed the afl tools, and learn to use it.
- Read the cJSON source code, modify the test program and use
alf-gcc
to compile it. Write a good test program is a challenge for me, I spend much time on it. - Fuzzed the cJSON project and found 3 kinds of crashes.
- Use to afl-get tool to analysis the output.
- I need to learn more about the working principles of afl and different crashes.
# Appendix 1: A Test program
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include "cJSON.h" /* Create a bunch of objects as demonstration. */ static int print_preallocated(cJSON *root) { /* declarations */ char *out = NULL; char *buf = NULL; char *buf_fail = NULL; size_t len = 0; size_t len_fail = 0; /* formatted print */ out = cJSON_Print(root); /* create buffer to succeed */ /* the extra 5 bytes are because of inaccuracies when reserving memory */ len = strlen(out) + 5; buf = (char*)malloc(len); if (buf == NULL) { printf("Failed to allocate memory.\n"); exit(1); } /* create buffer to fail */ len_fail = strlen(out); buf_fail = (char*)malloc(len_fail); if (buf_fail == NULL) { printf("Failed to allocate memory.\n"); exit(1); } /* Print to buffer */ if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) { printf("cJSON_PrintPreallocated failed!\n"); if (strcmp(out, buf) != 0) { printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n"); printf("cJSON_Print result:\n%s\n", out); printf("cJSON_PrintPreallocated result:\n%s\n", buf); } free(out); free(buf_fail); free(buf); return -1; } /* success */ printf("%s\n", buf); /* force it to fail */ if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) { printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n"); printf("cJSON_Print result:\n%s\n", out); printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail); free(out); free(buf_fail); free(buf); return -1; } free(out); free(buf_fail); free(buf); return 0; } int main(int argc, char *argv[]) { char buf[100]={0}; cJSON *root = NULL; gets(buf); root = cJSON_CreateObject(); cJSON_AddNumberToObject(root, buf, 1.0 / 0.0); if (print_preallocated(root) != 0) { cJSON_Delete(root); exit(EXIT_FAILURE); } cJSON_Delete(root); return 0; }
References
- https://afl-1.readthedocs.io/en/latest/fuzzing.html
- https://github.com/mykter/afl-training
- https://mp.weixin.qq.com/s/WMfCNN095-PpM0VB_pRESg
- https://github.com/rc0r/afl-utils
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse