State Selection Algorithms and Their Impact on The Performance of Stateful Network Protocol Fuzzing
Published
2022 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER)
Authors
背景
如图所示,被测试的服务器端系统或应用程序可以简单的抽象为四个层次,而对于大部分的协议模糊测试来说,都主要几种在程序调用图(call graph)和控制流图(control flow graph)层面上的探索算法,以提高模糊测试的有效性和效率。如针对路径(PATH) 、函数(Function) 、基本块(Basic Block)的研究。工作在最顶层状态模型常常被忽略。基于此,作者决定对状态选择算法进行研究。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XvGhz4Jp-1683858251236)(null)]
AFLNET
AFLNET作为协议模糊测试较为杰出的一个工具,主要支持三种编译算法:
- FAVOR:此算法会优先选择之前发现过新覆盖或漏洞的种子作为下一个测试用例,以期望再次触发相似的程序状态与数据流。这可以聚焦发现漏洞的测试路径。
- RANDOM:简单的随机选择算法,以一定概率随机选择一个未曾使用过的种子作为下一个测试用例。这种不相关的随机测试可以有效探索新的程序执行路径。
- ROUND-ROBIN:简单的轮流选择算法,以循环方式依次选择每个未使用种子,确保每个种子最终都会被选中作为测试用例。这种方式可以保证程序的每个种子最后都会被测试运行一遍。
可以看到这三种算法各有优势,AFLnet通过同时使用并动态调整不同算法的概率,可以在有导向性测试与广泛探索之间取得平衡。当发现新覆盖或Crash时,FAVOR的概率会瞬间提高,加大单一路径的测试强度。而在正常测试过程中,ROUND-ROBIN与RANDOM也适时参与,继续发掘新的程序路径与状态。
接下来作者对这三种算法进行测试,测试结果如下:
ProFuzzBench 中六个benchmark的24小时实验结果显示,三种算法在代码覆盖率方面取得了非常相似的结果。具体来说,所有基准测试的平均分支覆盖率之间的最大差异是1.15%
作者分析导致这几个算法出现这一结果的原因可能有两个:
- AFLNet 的状态机过于粗粒度,导致不可靠地估计每个状态的潜力
- 在协议模糊测试的脚本或测试用例生成算法设计中,探索(exploration)与利用(exploitation)之间的权衡(trade-off)十分关键,而现有算法则缺乏对这方面的权衡
- 探索指测试系统继续寻找与尝试新的输入值或测试路径,以发现程序的新覆盖或漏洞。这可以有效地扩展测试的广度与深度。
- 而利用则指测试系统聚焦已知的有效测试路径或输入模式,不断变异与重复利用,以期望再次触发相似的程序状态或漏洞。这可以加强测试的强度与效果
因此作者提出,如果解决了这两个问题,AFLNET的覆盖率是否能有所提升?
状态机
状态机用于对状态空间进行建模,是由有限个状态和状态之间的转换组成的图,在AFLNET中,它通过提取服务器的反馈代码来作为状态,如 “200 OK”, “400 ERR”。
蒙特卡罗搜索树
- 蒙特卡罗树搜索(MCTS)算法[21]已经证明了它在探索大搜索空间方面的有效性
- LEGION 提出了一种基于蒙特卡罗树搜索的原则性算法用于覆盖导向的普通软件测试。其MCTS的变体试图根据过去对代码覆盖的观察,在每次搜索迭代中学习最有希望的程序状态。LEGION将程序的符号树视为搜索空间,并使用四个略有变化的步骤从原始MCTS探索,以适应约束执行。
- 选择(Selection):从MCTS搜索树的根节点开始,逐层选择下行节点,直到选择一个仿真子节点。仿真子节点被添加到原搜索树的每个节点,以允许从其父节点的程序状态开始仿真,即使它们是中间节点。
- 仿真(Simulation):首先生成所选路径的输入,方法是求解仿真子节点的父节点的路径约束。从中间节点进行仿真。
- 扩展(Expansion):检查当前树中是否存在每个新执行路径,如果是新路径则将整个路径添加到树中并记录奖励。
- 传播(Propagation):将选择路径中所有节点的选择计数加1,并将奖励添加到执行路径中的所有节点的发现计数。然而,由于在实践中约束求解的误差,某些执行路径可能无法保留选择路径
方法
如上图所示分别为AFLNET和作者在AFLNET上进行的改进算法AFLNETLEGION的表现效果,AFLNET在记录状态时较为粗粒度,如执行MKD命令后执行完STOR命令实际应该在下一个目录,显示的NLST和在初始的250状态的NLST执行结果应该是不同的,但AFLNET仍然将它们当做是同一个状态,而AFLNETLEGION则对此进行了区分,以解决AFLNET对状态处理的粗粒度这一问题。
在AFLNETLEGION中,每个节点都由根到自身的唯一响应代码列标识。
如下图所示为AFLNETLEGION的执行过程,和上文提到的LEGON的过程相似:
其中,hollow node(圆形) 表示可以启动服务器的状态
simulation node (正方形)在空节点后表示从空节点发射模糊信号的选项
一个请求在一行中可能触发多个响应代码,用最后一个状态码表示的服务器状态启动fuzzing,而其他状态码则记为冗余节点,对模糊测试并不产生影响。
为了解决状态选择中的探索和利用的平衡,作者映入了UCT公式,如下所示:
对于节点计算,D、 S 和 PS 分别表示节点的过去发现计数、节点的过去选择计数和节点父节点的过去选择计数
对于种子评估,D、 S 和 PS 分别表示种子的过去发现计数、种子的过去选择计数和种子所属模拟节点的过去选择计数。
其中过去发现计数记录通过选择状态/种子发现的新响应序列的数量,过去选择计数记录为模糊化选择状态/种子的次数
效果
首先作者仔细挑选了ProFTPDf中的10个测试用例来进行测试,测试用例和结果如下:
测试用例:
测试结果
接下来作者在6个实际应用中的程序上进行测试,测试结果如下:
AFLNETLEGION _ UU 选择 UCT 分数最高的状态和种子
AFLNETLEGION _ UR 使用 UCT 进行状态选择并随机选择种子
AFLNETLEGION _ RR 随机选择状态和种子AFLNET_RR 随机选择种子和状态
对于OpenSSH,作者分析改进算法导致效果变差的原因如下:
-
OpenSSH违反了 AFLNETLEGION 关于获胜充分性的假设(即,找到唯一的响应代码序列)。从 OpenSSH 接收到一个独特序列的平均概率,AFLNETLE-GION 小于1.2% ,AFLNET 大约为2.5% 。
-
通过每个状态未被选中的频率来决定其潜力,从而进一步降低中标概率
-
MCTS 在顺序决策方面的优势在这个特定的基准程序中是无用的
不足
最后,作者总结了改进的算法依然无法带来更高的代码覆盖的原因可能有以下几个:
- 对于有状态协议模糊测试来说,模糊测试的吞吐量较低
- 生成的输入质量差
这些问题阻碍了状态选择算法充分发挥其潜力。
贡献
- 是第一个研究状态选择算法对有状态的网络协议的模糊测试的影响
- 将LEGION的算法扩展到了协议状态的选择上,并将其用于支持未来的研究。
总结
但是,通过数据可以看到,其实作者的实验效果并不很好,为了解决AFLNET对状态记录的粗粒度,作者选择用跟到自身的唯一响应码来记录节点,这在一定程度上也会带来冗余 ,同时也并不能说明作者的这一方法就能有效解决AFLNET状态处理粗粒度这一问题。同时在进行状态记录时,如果一个请求在一行中触发多个代码,作者只记录最后一个代码来表示服务器的响应,而丢弃其他状态码,通过OpenSSH的结果也可以看到这种方法存在很大的误差。
,作者选择用跟到自身的唯一响应码来记录节点,这在一定程度上也会带来冗余 ,同时也并不能说明作者的这一方法就能有效解决AFLNET状态处理粗粒度这一问题。同时在进行状态记录时,如果一个请求在一行中触发多个代码,作者只记录最后一个代码来表示服务器的响应,而丢弃其他状态码,通过OpenSSH的结果也可以看到这种方法存在很大的误差。
为了解决作者提到的AFLNET的第二个问题,没有平衡探索和利用的平衡,作者提出了一个公式,通过实验对比,也可以发现作者提出的这一算法对性能提升并不大。因此实际上,并不能证明作者已经解决了他提出的两个问题。