afl-fuzz漏洞检测实验

afl-fuzz漏洞检测实验

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.

    1589878959728

    afl-gcc and afl-g ++ correspond to the gcc and g ++ packages, respectively • afl-clang and afl-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 and afl-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 in cJSON project.

    sudo yum install cloc
    cloc .
    

    From this figure, we learn this cJSON project has 3594 code lines in two files (except test program).

1589859992627

  1. 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 changeCC = gcc -std=c89 to CC = /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

1589818121200

  • 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.

1589869272715

Analyze the output

What’s in the output directory ?

There are three subdirectories created within the output directory and updated in real time:

1589872684721

  • 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.

    1589869421918

  • 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.

    1589869910063

  • 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 (see afl-vcrash) as well as generating and executing gdb 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

1589889226629

  • 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

Select Texture

Hide

You can use the showPicker property in _config.yml to hide this picker permanently.

You can use the texture property in _config.yml to apply a texture permanently.