前言
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;
}
阅读源码,发现程序有三个崩溃点:
str
以\x00
开头str
以\x00
结尾- 存在“后一个数字是前一个数字+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。逐个来看看:
可见寻找到了全部三种漏洞。
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
类的逻辑。
- 构造函数,初始有两个乘员,分别是
Captain
和CoPilot
takeoff
,若机内有乘员,则起飞land
,若机内有乘员,则降落hire
,增加一个乘员fire
,减少一个乘员
若在乘员数量为 0 时要求飞机降落,则 abort。例子:
现在开始 fuzz,仍然采用 exercise 1 的随机输入文件:
记录了 7 个 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: