AFL++
AFL++
基础环境
由于源代码编译或包管理安装都有其弊端,稳定性不足或者功能不全,所以使用官方镜像安装,需要使用到Docker.
Docker
参考https://u.sb/debian-install-docker/
AFL++
sudo su #一定要是root权限
docker pull aflplusplus/aflplusplus
#把/location/of/your/target替换成自己需要挂载的路径.比如说我挂载到/mnt/soft/Docker 就是docker run -ti -v /mnt/soft/Docker:/src aflplusplus/aflplusplus
docker run -ti -v /location/of/your/target:/src aflplusplus/aflplusplus
进去 cd /src,就是你挂载的目录的内容.
使用测试
示例代码
点击查看代码
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define INPUTSIZE 100
int process(char *input)
{
char *out;
char *rest;
int len;
if (strncmp(input, "u ", 2) == 0)
{ // upper case command
char *rest;
len = strtol(input + 2, &rest, 10); // how many characters of the string to upper-case
rest += 1; // skip the first char (should be a space)
out = malloc(len + strlen(input)); // could be shorter, but play it safe
if (len > (int)strlen(input))
{
printf("Specified length %d was larger than the input!\n", len);
return 1;
}
else if (out == NULL)
{
printf("Failed to allocate memory\n");
return 1;
}
for (int i = 0; i != len; i++)
{
char c = rest[i];
if (c > 96 && c < 123) // ascii a-z
{
c -= 32;
}
out[i] = c;
}
out[len] = 0;
strcat(out, rest + len); // append the remaining text
printf("%s", out);
free(out);
}
else if (strncmp(input, "head ", 5) == 0)
{ // head command
if (strlen(input) > 6)
{
len = strtol(input + 4, &rest, 10);
rest += 1; // skip the first char (should be a space)
rest[len] = '\0'; // truncate string at specified offset
printf("%s\n", rest);
}
else
{
fprintf(stderr, "head input was too small\n");
}
}
else if (strcmp(input, "surprise!\n") == 0)
{
// easter egg!
*(char *)1 = 2;
}
else
{
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
char *usage = "Usage: %s\n"
"Text utility - accepts commands and data on stdin and prints results to stdout.\n"
"\tInput | Output\n"
"\t------------------+-----------------------\n"
"\tu <N> <string> | Uppercased version of the first <N> bytes of <string>.\n"
"\thead <N> <string> | The first <N> bytes of <string>.\n";
char input[INPUTSIZE] = {0};
// Slurp input
if (read(STDIN_FILENO, input, INPUTSIZE) < 0)
{
fprintf(stderr, "Couldn't read stdin.\n");
}
int ret = process(input);
if (ret)
{
fprintf(stderr, usage, argv[0]);
};
return ret;
}
afl-clang-fast -AFL_HARDEN=1 vulnerable.c -o vulnerable
fuzzing
使用afl-fuzz进行fuzzing,输入可以随便写,最好带有源码中的关键字,但须保证输入能使程序正常运行,即不能一开始使程序整个crash
mkdir in
mkdir out
echo 1 > in/1
echo "u 4 capsme" > in/2
afl-fuzz -i in -o out ./vulnerable

crash的使用与分析
栈的错误

sig:06 为程序自己调用abort终止自己的SIGABRT
sig:11 即段错误,是最常见的内存访问错误。通常由无效内存引用导致,比如说访问空指针、数组越界、访问野指针、栈溢出等.
所以尝试跟踪sig11的执行
使用gdb动调一下

这里可以看到崩溃发生在0x7fffffffe090处 下面要去查看这里附近的情况

发现错误发生在movzx r9d, byte ptr [rbx + rsi]这条指令运行时. 又可以看到rbx = 0x7fffffffe0af rsi = 0xf51
计算rbx + rsi = 0x7fffffffe0af + 0xf51 = 0x7ffffffff000
一般合法的栈范围在0x7fffffffxxxx附近 ,而此地址处于非法内存

可以看到这里的‘u 4’是符合源码的输入规定的,但是由于strtol()的解析规则一直读入数字直到字母,所以读到了28个4 也就是4.44×10²⁷,远超过long类型的最大范围(64 位系统中long最大约9×10¹⁸),所以结合源码,len的长度无论如何都会大于规定最大的input范围,相当于input的范围没有被限制了.
堆的错误
又重新跑了一会,出现了两个06

使用gdb调试一下 看看都是什么错误
id:00001

id: 00002

都是堆的问题,因为id 0001这个是可见字符 所以就使用第一个来分析.

知道这个是堆的错误 在strcat处下断点,运行后看堆的情况

在系统调用运行strcat之前 Top Chunk并没有遭到篡改,并且strcat是一个比较容易被利用的漏洞,猜测就是在这里内存被修改了.所以单步执行

main_arena的chunk确实被strcat这一步的执行影响到了,下面要看究竟是怎么影响到Top Chunk的大小的,从第二个chunk的起始位置看.

可以看到是从Top Chunk上一个chunk就已经开始对内存进行破坏了.原理和上面相同,都是因为输入数据的非法利用了strtol函数的机制,使值变得很大.再通过malloc里面的*rest偏移直接写入到这里,造成堆利用.

浙公网安备 33010602011771号