一、引言
Fuzz test(模糊测试),并不是一种新技术。早在1989年就被威斯康星州的麦迪逊大学BartonMiller教授发明已经有20年的历史。所谓模糊测试,即用随机坏数据(它是在正常数据上的微小变异)攻击一个程序,然后等着观察哪里遭到了破坏。模糊测试的技巧在于,它是不符合逻辑的,自动模糊测试不去猜测哪个数据会导致破坏,而是将尽可能多的fuzz数据交给被测对象。由这个测试验证过的失败模式通常对程序员来说是个彻底的震憾,因为任何按逻辑思考的人都不会想到这种失败。
长久以来对于模糊测试的应用,一般都集中在一些协议类的测试中,如ms word,通讯协议测试等,这类数据具有无结构、线性,一维的特点。百度大搜索同样面临稳定性测试的需求,但因众多后端模块输入的数据:网页,是一个有结构的,多维非线性数据。结构化的数据的fuzz一般需要通过“递归遍历”+“降维操作”+”流式处理”相结合的方式来替代传统的单纯流处理方式。这些数据一般都是异构的,原子元素大多不是同一类型,即使为同一类型,其长度或容量、内容大小等并无限制,而内容组成,以及原子元素间存在排列顺序、父子关系等多维语法的规则,无形增加变异的复杂度,同时遍历的加入、异构特性,可能意味着结构化Fuzz是一个不断发散无法收敛过程,时间消耗也会随之变得无法容忍。如何解决这个问题将fuzz引入网页测试,是我们目标。

二、工具原理
引言中提到了对于结构化的数据fuzz需要通过“递归遍历”+“降维操作”+“流式处理”。本小结,一起去看一下htmlfuzz工具的是处理这三个过程的。

整体操作流程图如下:
 

Htmlfuzz异常网页构造_异常



2.1递归操作
递归操作首先把html源码建成domtree结构,具体形式如下:
 

Htmlfuzz异常网页构造_网页_02



根据解析的domtree结构,建立遍历规则,使用的是深度优先遍历规则,进行了相应的一些容错处理,如:当出现不封闭标签时,根据一定的规则将标签封闭。当出现父子节点关系约束不成立时,向上找到可以满足父子关系的节点,将相应节点挂在其下(原则,尽量模拟浏览器行为)。
2.2降维处理
有了domtree和递归遍历domtree的规则,接下来,就需要对domtree进行降维操作了。首先要划分维度,对于html主要有如下几个要素:1、链接,锚文本 2、标签,标签属性,存文本内容,3、tree结构,tree结构深度,tree结构兄弟节点数量,4、html整体属性:网页大小,编码类型等等。以上几个维度是根据经验,从需求出发收集抽象出如下常见问题:
1.标签内purtext为空:空串处理问题
2.purtext内容长度过大:导致递归层次过多,导致桟溢出
3.兄弟节点过多:算法问题、模块运行速度急剧下降
4.标签层次过深:递归处理函数层次过多、导致桟溢出
5.标签属性异常:属性值为空、数目过大导致桟异常和对空指针操作
6.网页大小过大:算法问题,导致模块运行速度急剧下降
降维还有一个指标需要关注,即你需要在什么范围和容量内进行fuzz(如title的长度,你希望有长度范围在那里最合适,tree深度到什么程度是较理想的。)。统计这个指标有两个方式:1、根据html源文件的统计规则进行大规模数据挖掘(如:tree的平均深度和最深深度;深度较深的标签类型,anchor长度,url长度,)。2、根据工程师的经验进行配置。
我们采用了方式2,主要原因是在收益不明确的前提下,挖掘成本较大(后续会根据实际收益安排一些定向挖掘)。
最后,在降维上,抽象的部分具体的维度信息如下:
 

Htmlfuzz异常网页构造_休闲_03



 2.3流式处理
即所谓的fuzz规则。
1、字符串fuzz:
经过了上一轮的降维处理,部分点上的已经变成了字符串fuzz的需求了。如:锚文本变异,即为典型的字符串fuzz需求。
关于字符串fuzz,我们单独封装了一个函数进行处理,即把一个“任意的字符串A”传入“fuzzer函数”,函数return一个“fuzz之后的字符串B”。
我们通过静态库提供了若干个不同类型的“fuzzer函数”,这些函数分为两类:
1. 不管输出的字符串是什么,都直接return一个针对常见错误类型的异常字符串:
1) gen_BFO:针对Buffer Overflows的异常字符串。
2) gen_FSE:针对Format String Errors的异常字符串。
3) gen_INT:针对Integer Overflows的异常字符串。
2. 对输入的字符串做各种类型的修改,然后返回修改后的字符串:
1) insert_random_byte:在字符串中的一个随机位置插入一个随机的字节。
2) replace_random_byte:将字符串中的一个随机的字节替换为一个随机的字节。
3) replace_some_bytes:将字符串的一些随机的字节替换为另一些随机的字节。
4) del_random_byte:将字符串中的一个随机的字节删除。
5) insert_special_char:在字符串中的一个随机位置插入一个随机的“特殊字符”。
2、tree深度
左图为原始的dom 树,其中C标签为需要进行深度变异的标签,由于span标签可以作为任何标签的子标签,可嵌套在任何标签的内部,根据这一特性,htmlfuzz改变深度时,需要添加的大量嵌套标签,默认选择span标签,使得变异后网页仍然满足html规范要求。为增加C标签层次,具体实现方法是:先用一个span标签取代C标签的位置,然后以该标签为父结点,根据用户配置的标签层次要求生成若干层的span子标签,最后将以C为根的子树作为最后一个span标签的子树,右图为深度变异后的dom树。htmlfuzz可以根据用户选择的标签填充嵌套位置,达到层次深度变异,但用户选择的标签嵌套必须符合html的规范。
 

Htmlfuzz异常网页构造_Htmlfuzz_04



3、网页大小变异
(1) 将dom树内容写到缓冲区,计算网页的大小是否满足用户需求,如果不满足,计算目标网页大小和实际网页大小的差值。
(2) 随机选择dom树中一个文本长度大于2的标签,利用不断复制其自身文本内容以达到增大网页整体长度的目的。因为网页本身有各种编码形式,为了保证网页整体编码一致性,变异后的结果不会出现文本乱码,随机选择目标标签,只进行内容自动复制。

三、使用方法
3.1基本命令
命令如下:./alpha_htmlfuzz [-p] pack [-w] page [-o]
[p] 参数指定输入为pack
[w]参数指定输入为网页
[o] 参数指定输出到屏幕上
htmlfuzz目前支持的组合有以下几种:
1、输入为pack包 ,输出到屏幕
例如:./alpha_htmlfuzz -p test.pack -o
2、输入为pack包,输出到新的pack包
例如:../alpha_htmlfuzz -p test.pack
(输出文件和test.pack在同样的文件夹内,输出pack包的命名规则为test.pack-new_pack,即源文件-new_pack)
3、输入为单个网页,输出为网页集合
例如: ./alpha_htmlfuzz -w test.html
(输出文件在../output文件夹内,输出网页的命名规则:变异函数名_循环次数_原网页名)
4、输入为文件夹,输出为网页集合(htmlfuzz会遍历该文件夹内所有网页)
(输出文件在../output文件夹内,输出网页的命名规则:变异函数名_循环次数_原网页名)
3.2初级用户指南
初级用户只需要掌握上述基本命令即可。htmlfuzz已默认为用户设置好各种变异的规则 ,可满足通用的需求。
每种变异都设置了开关选项,用户可以修改switch.conf配置文件来决定执行哪些变异种类。在switch.conf配置文件中一共有10种类型的开关,其中ON代表打开该变异功能呢个,OFF代表关闭该变异。
FUZZ_LINK_NUM : ON (对链接数目的变异)
FUZZ_LINK_ANCHOR : ON (对锚文本的变异)
FUZZ_LINK_URL : ON (对URL的变异)
FUZZ_TAG_LEVEL : ON (对标签深度的变异)
FUZZ_TAG_NUM : ON (对标签数目的变异)
FUZZ_TAG_DATA : ON (对标签内容的变异)
FUZZ_TAG_ATTRIBUTE : ON (对标签属性的变异)
FUZZ_HTML_SIZE : ON (对网页整体大小的变异)
FUZZ_HTML_CHARACTER : ON (对字符集的变异)
FUZZ_HTML_WHOLEFUZZ : ON (破坏网页规则的变异)
3.3高级用户指南
对于高级用户来说,可以修改control.conf配置文件来设置各种变异参数的数值,以生成满足用户特殊需求的网页。
Control.conf文件中各选项的配置为:
#变异大循环的次数 一个网页生成的变异网页的个数为:CYCLE_INDEX*变异的种类,如果用户想用少量网页生成大数据量网页的话,可以将该参数调大。
CYCLE_INDEX : 1
----------------------------------------------------------------------
#网页中链接的个数(htmlfuzz会构造1000个含A标签的网页)
LINK_NUM : 1000
----------------------------------------------------------------------
#链接的最大长度 (htmlfuzz会构造长度为1024的URL,并且会把这些特殊字符集加入到URL中)
URL_LENGTH : 1024
#特殊字符集,用于构成URL的特殊字符
SPECIAL_CHARRACTER : #%$?-_&:=,`^*@+()
----------------------------------------------------------------------
#锚文本最大长度 (htmlfuzz会构造某个锚文本内容为1024的网页)
ANCHOR_LENGTH: 1024
----------------------------------------------------------------------
#XPATH字段可以定制多个XPATH(绝对路径,相对路径都可以),以空格分隔
#程序会从XPATH路径中随机选择一个XPATH路径作为变异的XPATH
XPATH : p div span a h1 h2 h3 strong /html/body/div/p p/strong
#满足XPATH路径的某tag标签变异的深度(htmlfuzz会随机挑选一个满足用户定制XPATH路径的标签,将以该标签为根的子树移动1000层,其中新增层次的标签由NESTED_CANDIDATE_TAGS选项配置)
TAG_LEVELS : 1000
#当tag标签的深度加深时,新增加的标签的名称
NESTED_CANDIDATE_TAGS : span li p
----------------------------------------------------------------------
#满足XPATH路径的某tag标签兄弟节点数目(htmlfuzz会随机挑选一个满足用户定制XPATH路径的标签,会使该标签增加100个同类的兄弟节点)
TAG_BROTHER_NUM : 100
----------------------------------------------------------------------
#满足XPATH路径的某tag标签的属性数目(htmlfuzz会随机挑选一个满足用户定制XPATH路径的标签,使得该标签有1000个属性,其中属性键key的最大长度为50.,属性value的最大长度为50)
TAG_ATTRIBUTE_NUM : 1000
#该标签属性key的最大长度
TAG_ATTRIBUTE_KEY_LEN : 50
#该标签属性value的最大长度
TAG_ATTRIBUTE_VALUE_LEN : 50
----------------------------------------------------------------------
#满足XPATH路径的某tag标签所指向文本的内容长度(htmlfuzz会随机挑选一个满足用户定制XPATH路径的标签,使得该标签的内容长度为1024)
TAG_DATA_LENGTH : 1024
----------------------------------------------------------------------
#HTML网页的大小(支持的单位为M,K)(数字后不加单位,则以字节为单位)(htmlfuzz会构造10M的网页)
HTML_SIZE : 10M
----------------------------------------------------------------------
#HTMLFUZZ目前支持的编码有:UTF-8,GBK,BIG5,HZ,GB18030,GB2312
#网页的实际编码格式 (htmlfuzz会构造实际编码为GB18030,且charset字段的编码为BIG5的网页)
HTML_REAL_ENCODE : GB18030
#网页head标签中的charset字段
HTML_CHARSET : BIG5
3.4定制用户指南
htmlfuzz目前的变异子函数的设计:
htmlfuzz根据变异子函数的参数的不同,将所有变异子函数放入到两个向量中,
1、 vector<int (*) (const char * ,bool,void *,int)> no_dom_vec;(这种回调函数虽然有4个参数,但这4个参数都是固定的,用户只需仿造其他此类型函数的参数即可。这四个参数依次为网页内容的指针,选择输出到pack或网页的开关,pack相关信息的结构体,第几次大循环;其中第3个参数的内容如下。
typedef struct
{
ul_package_t *in_pack; //已经分配
ul_package_t *out_pack; //已经分配,但是每次用时最好清理
int *out_fd; //已经打开 pack包文件所对应的文件指针
char *out_buf; /已经分配 为pack申请的buffer动态内存
}pack_node;
2、vector<int (*) (html_tree_t &)> vec;
(该回调函数的参数为DOM树的引用)(此函数实际上有个开放的全局输入参数vector<html_node_t *> vec_tag,该向 量存放满足用户定制的XPATH要求的所有标签的指针,用户可以直接使用该变量)
用户添加子函数的过程
1、在alpah_fuzz.cpp中的main_for_every_fuzz函数中,将新定义的变异子函数添加都相应的变异子函数向量中(vec或者no_dom_vec)
 

Htmlfuzz异常网页构造_休闲_05



上图中bit_switch是bitset类型数据,是为了控制变异子函数是否执行的开关。
2、编写变异子函数
 

Htmlfuzz异常网页构造_构造_06


该子函数的功能是给满足用户定制的XPATH路径的标签添加一个兄弟节点。
用户在维护DOM树的时候,重点关注一下next指针和child指针,其他指针可以无视。(因为在将DOM树打印到网页时,是采用递归的方法,只用了DOM树节点的next指针和child指针)。

由于程序运行过程中需要申请大量的空间,因此将每次动态申请的内存都放入以下三个向量中,便于管理。在每个变异子函数结束时,htmlfuzz都会清理动态申请的空间。
vector<html_node_t *> garbage_node;
vector<char *> garbage_char;
vector<html_attribute_t* > garbage_attribute;
3,第1步和第2步走完,基本上已添加完新的变异。
 

(作者:caizhaowen)

【本文转自百度测试技术空间http://hi.baidu.com/baiduqa/blog/item/4dff7d283d509a3ed42af142.html