前言

  AFL++ 文档推荐了一个 newbie guide 项目:https://github.com/alex-maleno/Fuzzing-Module。里面一共有三个练习,算是很详细的教程。本文是这三个练习的 writeup。

Exercise 1

  源码如下:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

int main() {

   string str;

   cout << "enter input string: ";
   getline(cin, str);
   cout << str << endl << str [0] << endl;

    if(str[0] == 0 || str[str.length() - 1] == 0) {
        abort();
    }
    else {
        int count = 0;
        char prev_num = 'x';
        while (count != str.length() - 1) {
            char c = str[count];
            if(c >= 48 && c <= 57) {
                if(c == prev_num + 1) {
                    abort();
                }
                prev_num = c;
            } 
            count++;
        }
    }
    
    return 0;
}

  阅读源码,发现程序有三个崩溃点:

  1. str\x00 开头
  2. str\x00 结尾
  3. 存在“后一个数字是前一个数字+1”的情况,例如 aaa7aaaa8aa

  接下来开始 fuzzing。按照教程,生成一些随机输入:

dd if=/dev/urandom of=1.in bs=64 count=10
dd if=/dev/urandom of=2.in bs=64 count=10
dd if=/dev/urandom of=3.in bs=64 count=10

  这样就造好了三个 640B 的随机输入文件。按照教程要求,采用 afl-clang-fast++ 来编译:

afl-clang-fast++ simple_crash.cpp -o target_fast

  开始 fuzz:

  保存了 3 个 crash。逐个来看看:

▲ 以 \x00 开头
▲ 存在“3...4”
▲ 以 \x00 结尾

  可见寻找到了全部三种漏洞。

Exercise 2

  源码如下:

// airplane_object.cpp
/*
 * George Sidamon-Eristoff
 * 30 March 2022
 * Comp 98 - Senior Capstone, 2nd Semester
 * airplane_object.cpp contains the function contracs and definitions for the Airplane class
 *
 * Note: there are purposely not many comments so the bug is more difficult to find
 */

#include <iostream>
#include <string>

#include "airplane_object.hpp"


Airplane::Airplane(){
    crew.num = 2;
    crew.captain_name = "Captain";
    crew.copilot_name = "CoPilot";
}


void Airplane::takeoff(){
    if (crew.num >= 1) {
        std::cout << "Takeoff!\n";
    } else {
        std::cout << "Nobody to fly the plane!\n";
    }
    return;
}

void Airplane::land(){
    if (crew.num >= 1) {
        std::cout << "Landed safely!\n";
    } else {
        std::cout << "Nobody left to land the plane!\n";
    }
    return;
}

void Airplane::hire(){
    crew.num += 1;
    std::cout << "Welcome aboard!\n";
    return;
}


void Airplane::fire(){
    crew.num -= 1;
    std::cout << "You're out of here!\n";
    return;
}

void Airplane::interact() {
    std::string input  = "";
    getline(std::cin, input);
    int input_length = input.length();
    if (input.empty()) {
        std::cout << "No command.\n";
    } else {
        int i = 0;
        while (input[i] != 'q' and i < input_length) {
            // std::cout << "i = " << i << "  input[i] =" << input[i] << "\n";
            if (input[i] == 't') {
                takeoff();
            } else if (input[i] == 'l') {
                if (crew.num == 0) {
                    abort();
                }
                land();
            } else if (input[i] == 'h') {
                hire();
            } else if (input[i] == 'f') {
                fire();
            }
            i++;
        }
    }
    return;
}
// main.cpp
/*
 * George Sidamon-Eristoff
 * 30 March 2022
 * Comp 98 - Senior Capstone, 2nd Semester
 * main.cpp is the file through which the user will interact with this program
 *
 * The goal of this program is to serve as a "medium-complexity" target to fuzz
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#include "airplane_object.hpp"


int main(int argc, char* argv[]){
  if (argc != 1) {
    std::cout << "Error" << std::endl;
  } else {
    std::cout << "\nInput a string made of the following characters: 't', 'l', 'h', and 'f'" << std::endl;
    std::cout << "Any / all other characters will be ignored.  'q' quits the application." << std::endl;
    std::cout << "t == take off, l == land, h == hire a crew member, f == fire one.\n" << std::endl;
    Airplane* my_airplane = new Airplane();
    my_airplane->interact();
    delete my_airplane;
  }

  return 0;
}

  这个 main.cpp 是个 CLI 包装,主要看 Airplane 类的逻辑。

  • 构造函数,初始有两个乘员,分别是 CaptainCoPilot
  • takeoff,若机内有乘员,则起飞
  • land,若机内有乘员,则降落
  • hire,增加一个乘员
  • fire,减少一个乘员

  若在乘员数量为 0 时要求飞机降落,则 abort。例子:

  现在开始 fuzz,仍然采用 exercise 1 的随机输入文件:

  记录了 7 个 crash。去看看:

▲ 第一个 crash,有 f..f...l
▲ 第二个 crash,比第一个 crash 规整一些

  后面的几个 crash 样本与前两个类似,略。

Exercise 3

  根据教程,我们利用 Sourcetrail 来观察代码。首先,生成 compile_commands.json

CC=afl-clang-lto CXX=afl-clang-lto++ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..

  本章节的教程是 slice fuzzing,即裁剪原程序,只针对某一特定部件进行 fuzz。来看样例 slice:

#include "specs.h"

int main(int argc, char** argv) {
    // In order to call any functions in the Specs class, a Specs
    // object is necessary. This is using one of the constructors
    // found in the Specs class.
    Specs spec(505, 110, 50);
    // By looking at all the code in our project, this is all the 
    // necessary setup required. Most projects will have much more
    // that is needed to be done in order to properly setup objects.

    // This section should be in your code that you write after all the 
    // necessary setup is done. It allows AFL++ to start from here in 
    // your main() to save time and just throw new input at the target.
    #ifdef __AFL_HAVE_MANUAL_CONTROL
        __AFL_INIT();
    #endif

    spec.choose_color();

    return 0;
}

  即是创建一个 Specs(505, 110, 50) ,然后调用 choose_color() 。这里有一个 __AFL_INIT。按文档描述,默认情况下,AFL++ 的策略是:目标运行到 main() 前面时暂停,以此为基础 clone 出进程以供 fuzz。这节省了不少时间,但是如果在 fuzz 的关键步骤前面有诸如“载入配置文件”等步骤,仍然会造成效率浪费。因此,我们可以自行选择 fuzz 入口,然后添加以下代码:

#ifdef __AFL_HAVE_MANUAL_CONTROL
  __AFL_INIT();
#endif

  fuzz 结果如下:

  跟进 choose_color(),发现如果 input 是纯数字串,就会 abort:

bool Specs::isNumber(std::string str)
{
    for (int i = 0; i < str.length(); i++) {
        if (isdigit(str[i]) == 0) 
            return false;
    }
    return true;
}

// ...

void Specs::choose_color() {
    std::string color;
    std::cout << "enter desired aircraft color: ";
    std::cin >> color;
    if (isNumber(color))
        abort();
}

  crash 如下:

▲ 空串
▲ 纯数字串

  现在我们自己来写一个 slice,测试 fuel_cap() 方法:

#include "specs.h"

int main(int argc, char** argv) {
    Specs spec(505, 110, 50);
    
    #ifdef __AFL_HAVE_MANUAL_CONTROL
        __AFL_INIT();
    #endif

    spec.fuel_cap();

    return 0;
}

  crash:

▲ 666666 超出范围,-666 引发 crash