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偏移直接写入到这里,造成堆利用.

posted @ 2025-10-21 12:06  awigwu76  阅读(25)  评论(0)    收藏  举报