CLIFuzzer
CLIFuzzer:命令行调用的挖掘语法
很多程序都靠命令行来传递选项和参数,因此要对这些程序做测试,需要遍历所有的命令行选项。CLIFuzzer使用动态分析来跟踪输入,并自动提取参数,我们可以变换不同的输入参数,生成无穷的随机参数列表,对程序进行模糊测试,提高代码覆盖率。
注:与之很相似的有基于变异的fuzzer,它通过对输入数据进行随机变异和扰动来寻找软件的异常行为。这种模糊测试方法基于以下假设:在输入数据中的细微变化和异常情况可能会引发软件中的漏洞。
命令行程序输入参数的格式为$<程序名> <parameter>*
。其中,<parameter>
要么为参数名,例如-v|-a
,要么为参数值,例如1.txt
等。目前,已经有很多研究关注于命令行程序,其局限性在于:只测试标准输入,即要么忽略程序接受的不同选项,要么只用特定的选项序列。还有的研究通过分析--help
来获得选项集,但是--help
选项可能不存在。
而CLIFuzzer可以从代码中自动确定选项列表,这基于一个假设:大多数的程序使用类似于getopt()
之类的选项parser。在本论文中,主要是针对python的argparse进行原型设计,以创建命令行参数的语法。进一步地,我们将上述原型转为针对C语言的,使用getopt()
来进行模糊测试的fuzzer。但是,getopt()
无法获得选项类型,因此,我们通过跟踪运行时库函数(runtime library function
)来获得选项类型。最终,CLIFuzzer获得了能准确描述程序参数的一套语法,如下所示,ls
命令的语法为:
CLIFuzzer利用这套语法创建无穷的命令行序列,来测试程序。
getopt()
函数
标准C语言库函数parse命令行主要用getopt | getopt_long | getopt_long_only
函数。这些函数中的两个参数定义可能的选项:
(1)optstring
。保存程序的短选项(例如-a
)。举个例子,optstring="1ac:d::"
,这代表一个短选项列表,见链接。选项类型分为3种:(a)不带值的参数,其定义就是参数本身;(b)必须带值的参数,在定义后加:
;(c)可选值的参数,在定义后加::
。其表示语法如下(其中<prefix>
不知道是什么?):
(2)longopts
。指向选项结构体的指针,描述了程序接受的长选项。从结构体为:
参数规范分析
此节的目的是生成参数语法。其步骤为:(1)将选项字符串转为上下文无关的语法。(2)将选项参数转为谓词。(3)参数到谓词。(看不太明白,接着向下看)
从选项规范(optstring、longopts)构建语法
此步骤将短选项与长选项规范转为上下文无关的语法。我们将getopt
函数更改,从而记录参数。更改后的getopt
被加入到共享库中,并重写LD_PRELOAD
环境变量,以加载此共享库。
一旦CLIFuzzer提取到了optstring
,就会用如下算法将短选项转为语法。
上述算法中,首先检查optstring
是否以'-'
开头。如果是,则表明该程序接受任何未指定的选项字母,而不会立即出现错误,这部分在第 2 节中详细描述。因此,我们将-<letter>
附加到上下文无关语法中。如果optstring
以:
或+
开头,它将影响向程序指示缺失参数的方式,具体见链接。然而,它对选项规范没有直接影响,因此跳过。longopts
不需要parse,直接转为语法即可。
挖掘选项参数类型
CLIFuzzer扫描libc
,以找寻需要参数的函数。它重写每一个libc
的函数,以便调用这些函数时会记录参数。CLIFuzzer使用每个选项的随机参数来调用被测程序(如何理解?),举个例子,如果文件名为参数,那么程序可能会调用open|stat
函数;如果以整数为参数,那么程序可能调用atoi|strtol
函数;如果以浮点数为参数,那么程序可能调用atof|strtod
。
参数的谓词
最后一步就是找到参数要满足的条件(谓词)(理解的不一定对)。因此,使用多个参数来调用实用程序来确定程序需要多少个参数。
评估
程序最起码有一个文件输入,保证完全随机性。选项个数平均数为27,代码量平均为1w行。以AFL++为基线,当程序具有大量有效选项时,CLIFuzzer的覆盖范围明显优于AFL++,因为选项不是AFL++模糊测试过程的一部分。当程序没有有效选项(什么叫有效选项?)时,CLIFuzzer与AFL++的覆盖率相当。但是,CLIFuzzer并不能找到col程序中的crash部分,因为这需要特定字符作为输入,但是AFL++可以,这是因为它的目标就是更高的代码覆盖率。
突然感觉fuzzer的目的是让程序崩溃?
举几个CLIFuzzer找到的崩溃点:
(1)bison
(接受一个上下文无关语法规范作为输入,并生成一个解析器,用于分析输入的语法结构):当运行bison --trace s1
时,会挂起。其中,--trace
选项并未在使用文档中提到。
(2)tac
(反向输出):当运行tac --separator=.+5 --regex E.coli
时会挂起,程序会卡在regexec.c
中,其中csplit|expr|nl
中也调用了此文件,因此猜测这些指令中可能也会出现这种毛病。
相关工作
AFL(Americal Fuzzy Lop)主要关注stdin
与文件输入,不太关注参数输入。相关变体有AFLGo|AFL++
。
AFL++
中有一个实验性的参数fuzz,但是并不是专门针对创建选项,因此用处不大。RIDDLE
利用程序的一些选项(通过语法)来进行fuzz(不知道和CLIFuzzer的区别)。iFUZZ则需要用户主动提供getopt()
中的optstring
参数。Wang等人在在CLI程序上选择某些引导模糊测试的选项,其具体目标是最大化覆盖率,选项在Protobuf中指定为语法,并指导模糊测试工具进行fuzz。Lee首先从程序文档中提取了一组选项,之后他们确定了最大化覆盖率的选项子集,这些选项用于构造十个调用字符串,然后用于对程序进行模糊测试,在此期间,仅输入文件发生变化。与CLIFuzzer相比,上述所有方法都需要一定的人力来推断完整的命令行调用(感觉优势不是很明显)。
论文代码
https://github.com/vrthra/fse2022-clifuzzer
To be continued…
留言
- 文章链接: https://wd-2711.tech/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!