0x01 fuzz工具集合
1、AFLplusplus
AFL实战
https://blog.csdn.net/qq_3671...
AFL整体流程:
https://blog.csdn.net/yalecal...
https://xz.aliyun.com/t/4314
一篇好的文章:
https://www.freebuf.com/artic...
各种语料:https://github.com/strongcour...
网络模糊测试:https://www.secpulse.com/arch...
https://wanghenshui.github.io...
https://xz.aliyun.com/t/7643
对那些可以直接从stdin读取输入的目标程序来说,语法如下:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program […params…]
对从文件读取输入的目标程序来说,要用“@@”,语法如下:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
ex:
* mkdir in out
* cd in
* cp afl-2.52b/testcases/others/elf/small_exec.elf . #afl目录中自带一些常用文件的testcase
* cd ..
* sudo cp /usr/bin/readelf . #把readelf复制到当前目录中来
* afl-fuzz -i in -o out -Q ./readelf -a @@ #开始fuzz,@@表示从in文件夹中找elf作为输入,实际上就是在执行:readelf -a 文件名
1.1、AFL++ 数据含义
map density 哈希表密度
afl使用共享内存,完成分支覆盖信息在fuzzer和target之间的传递。共享内存被用于保存一张哈希表。当target执行结束,fuzzer会对该表进行分析
cycle done
当队列中的全部文件都变异测试后,就完成了一个”cycle”,这个就是AFL状态栏右上角的”cycles done”。而正如cycle的意思所说,整个队列又会从第一个文件开始,再次进行变异,不过与第一次变异不同的是,这一次就不需要再进行deterministic fuzzing了。
2、boofuzz
安装:https://github.com/jtpereyda/...
文档:
https://boofuzz.readthedocs.i...
3、libfuzzer
官方文档:https://llvm.org/docs/LibFuzz...
各种实践:https://github.com/Dor1s/libf...
4、不同应用场景
java模糊测试
https://github.com/rohanpadhy...
https://github.com/isstac/kel...
数据库模糊测试
https://github.com/s3team/Squ...
API fuzz
待评估
https://github.com/dzonerzy/P... 很久没更新了,支持python2
https://github.com/smasterfre...
https://github.com/Fuzzapi/fu...
0x02 协议测试
1、需要关注的点
协议知识:https://www.cnblogs.com/feiti...
- 名字-值对:对值进行模糊测试
- 块标识符:块标识符后通常紧跟边长或定长的数据,通过对块标识符进行模糊测试,可以发现一些文档中没有记录的块识别符,这些文档中没有记录的块标识符很可能会接受额外的数据类型,并带来安全风险
- 块大小:修改数据包中标识块大小的值,使其比实际数据块的数据大小稍大或稍小一些,然后观察结果输出。缓冲区溢出只要通过这种模糊测试方法发现的。如果要替换块中的数据进行模糊测试,一定要根据实际数据的大小调整块大小的值,只有这样,被测应用才能准确识别数据块中的数据。
- 校验和:有些文件格式在文件中嵌入了校验和(例如PNG图像格式)。在对带校验和的文件格式进行模糊测试时,模糊测试器可以自行计算校验和并将其写入文件,这样可以保证被测应用能够正常处理用于模糊测试的文件。
2、HTTP协议
模糊测试数据集需要包含的特殊字符
!@#$%^&*()-_=+{}|\;:'",<.>/?~`
3、协议分析
wireshark的subversion库,直接跳到epan\dissectors
0x03 深入理解
afl源码分析
参考文档:https://www.jianshu.com/p/069...
https://www.jianshu.com/p/c70041b8026e
https://blog.csdn.net/qq_32464719/article/details/80592902
http://rk700.github.io/2017/12/28/afl-internals/
https://blog.csdn.net/u012503639/article/details/104128658
插桩
目的:检测覆盖率
如果我们想要了解一个程序在某次运行中可执行语句被覆盖的情况,或是每个语句的实际执行次数,最好的办法就是利用插装技术。
在AFL编译文件时候afl-gcc会在规定位置插入桩代码,可以理解为一个个的探针(但是没有暂停功能),在后续fuzz的过程中会根据这些桩代码进行路径探索,测试等。
AFL通过插桩的形式注入到被编译的程序中,实现对分支(branch、edge)覆盖率的捕获,以及分支节点计数。代码大致如下:
cur_location = <COMPILE_TIME_RANDOM>;
shared_mem[cur_location ^ prev_location]++;//将当前块和前一块异或保存到shared_mem[]
prev_location = cur_location >> 1;//cur_location右移1位区分从当前块到当前块的
阶段
afl-gcc是对gcc的一个封装(afl-gcc最终会调用afl-gcc)
gcc /tmp/hello.c -B /root/src/afl-2.52b -g -O3 -funroll-loops -D__AFL_COMPILER=1
-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
插桩过程中会在as(linux下常用汇编器)的基础上调用afl-as,afl-as是对.s文件(汇编代码)进行插桩操作,在其中有跳转的的位置插入汇编码,实现在程序跳转时能够通过在跳转处的插桩掌握程序的执行路径,具体插入的代码如下
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE));
static const u8* trampoline_fmt_32 =
"\n"
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
"\n"
".align 4\n"
"\n"
"leal -16(%%esp), %%esp\n"
"movl %%edi, 0(%%esp)\n"
"movl %%edx, 4(%%esp)\n"
"movl %%ecx, 8(%%esp)\n"
"movl %%eax, 12(%%esp)\n"
"movl $0x%08x, %%ecx\n"
"call __afl_maybe_log\n"
"movl 12(%%esp), %%eax\n"
"movl 8(%%esp), %%ecx\n"
"movl 4(%%esp), %%edx\n"
"movl 0(%%esp), %%edi\n"
"leal 16(%%esp), %%esp\n"
"\n"
"/* --- END --- */\n"
"\n";
最终再调用as进行真正的汇编。在函数add_instrumentation()中对输入的汇编代码.s文件进行操作,下列代码将识别跳转(je,jnz之类)
例子
int main(int argc, char *argv[])
{
if(argc>1)
printf("yes");
else
printf("no");
return 0;
}
使用afl-gcc插桩后反汇编代码如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char **v4; // [rsp+8h] [rbp-98h]
v4 = envp;
_afl_maybe_log(*(_QWORD *)&argc, argv, envp, 62392LL);
__asm { endbr64 }
if ( argc <= 1 )
_afl_maybe_log(*(_QWORD *)&argc, argv, v4, 17798LL);
else
_afl_maybe_log(*(_QWORD *)&argc, argv, v4, 62291LL);
sub_1110();
return 0;
}
ASAN分析
参考:https://blog.csdn.net/weixin_...
ASAN工具主要由两部分组成
编译器插桩模块
运行时库
运行时库:(libasan.so.x)会接管malloc和``free函数。malloc执行完后,已分配内存的前后(称为“红区”)会被标记为“中毒”状态,而释放的内存则会被隔离起来(暂时不会分配出去)且也会被标记为“中毒”状态。
编译器插桩模块:加了ASAN相关的编译选项后,代码中的每一次内存访问操作都会被编译器修改为如下方式:
编译前:
*address = ...; // or: ... = *address;
编译后:
if (IsPoisoned(address)) {
ReportError(address, kAccessSize, kIsWrite);
}
*address = ...; // or: ... = *address;
该方式的关键点就在于读写内存前会判断地址是否处于“中毒”状态,还有如何把IsPoisoned实现的非常快,把``ReportError实现的非常紧凑,从而避免插入的代码过多。
ASan对缓冲区溢出防护的的基本步骤如下:
通过在被保护的栈、全局变量、堆周围建立标记为中毒状态(Poisnoned)的red-zones;
以栈缓冲区溢出检测为例,将缓冲区和red-zone通过每8字节对应1字节的映射的方式建立影子内存区,影子内存区的获取函数为MemToShadow。 如果出现对red-zone的读、写或执行的访问,则ASan可以ShadowIsPoisoned检测出来并报错。
报错信息给出出错的源文件名、行号、函数调用关系、影子内存状态。其中影子内存状态信息中出错的部分用中括号标识出来。如下所示:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。