driller 符号执行+fuzz漏洞挖掘思路
http://cs.ucsb.edu/~chris/research/doc/ndss16_driller.pdf
先看一段程序
int main()
{
config_t* config=read_config();
if (config==null){
puts("config error\n");
return -1;
}
if (config->magic!=MAIGIC_NUMBER){
puts("magic number error\n");
return -1;
}
initialize(config);
char* directive=config->directive[0];
if(!strcmp(directive,"crash_string")){
program_bug();
}
if(!strcmp(directive,"set_config"){
program_bug();
}
else{
default();
}
}
fuzz engine首先fuzz第一部分read_config(),initialize()
遇到比较magic number的程序段,调用 concolic execution engine来找到能通过检测的输入,来到达程序的其他部分
fuzz engine找到default()
concolic engine找到满足约束条件的输入来达到program_bug()
仅仅通过fuzz和符号执行本身是无法找到program_bug(),initialize()中有非常多状态,
这会导致路径爆炸,导致符号执行失败,在遇到比较MAIGIC_NUMBER时,传统的fuzz会失败,
其他一些常见的编程方式,如比较hash值,也会导致fuzz失败。
因此fuzz engine和concolic engine的混合使用有较高的潜力。这里选择afl qemu作为
fuzz engine
driller用到的afl的一些特性
afl特性
- 通过遗传启发算法对输入进行变异,用适应度函数对他们进行排名.unique函数基于独特的代码覆盖率,即触发与其他输入触发的路径不同的执行路径。
- AFL跟踪它从输入中看到的控制流转换的联合,作为源和目标基本块的元组。基于他们发现的新的控制流转换,遗传算法中的输入被初始化为“繁殖”。这意味着导致应用程序以不同方式执行的输入会在未来输入的生成中获得优先权。
- 处理循环对于模糊引擎和concolic执行引擎来说是一个复杂的问题。为了帮助减小循环的路径空间的大小,执行以下启发式。当AFL检测到路径包含循环迭代时,将触发次级计算以确定该路径是否有资格进行繁殖。AFL确定执行的循环迭代次数,并将其与先前导致路径经历相同循环的输入进行比较。这些路径全部通过其循环迭代次数的对数(即1,2,4,8等)被置于“桶”中。在遗传算法中考虑来自每个桶的一条路径用于繁殖。这样,每个循环只需要考虑log(N)条路径,而不是N条。
- 程序随机化干扰遗传模糊器对输入的评估 - 在给定随机种子下产生有用路径的输入可能不会在另一个下产生。我们预先将AFL的QEMU后端设置为特定的随机种子,以确保一致的执行。
- 之后,当发现崩溃的输入时,我们使用concolic执行引擎来恢复任何错误“挑战 - 回应”行为或依赖泄漏随机性的漏洞。例如,二进制文件中的“质询 - 响应”过程会将随机数据回传给用户,并期望将相同的数据回传。在不去除随机化的情况下,模糊组件可能每次都会通过这个检查并探索很少的路径。如果随机性不变,程序每次都接受相同的输入,让模糊器(或concolic执行组件)可以自由地找到这个值并随后进一步探索。发现崩溃后,随机性可以像第V-D4节所述的那样象征性地建模,并且崩溃输入可以相应地进行修补。这些功能允许AFL通过应用程序快速发现唯一路径,从而在应用程序的指定隔间内执行路径发现工作。但是,模糊的局限性是众所周知的。