1 介绍
现有fuzzer最主要的挑战是:许多fuzz执行少数相同路径。举个栗子,fuzzing一张有效的图片文件,那么变异后的图片有90%的可能性执行拒绝无效图片文件的路径 π \pi π。fuzzing一张无效的图片文件,有99.999%的概率变异之后的文件执行相同的路径 π \pi π。我们称路径 π \pi π为high-frequency路径。
所以本文工作提出一些策略来帮助在相同的fuzz次数下探索low-frequency路径。实验结果表明我们的AFL工具在GUN binutils中发现9个漏洞,他们被在US National漏洞数据库中列为CVEs(Common Vulnerabilities & Exposures,常见的漏洞和风险)。AFLFAST发现6个CVEs的速度是AFL的14倍,发现3个在8次运行24小时的fuzz中AFL没发现的CVEs。
AFLFast的核心思想(似乎?)是对初始化的种子非常低的能量,每当种子被选择后(TM看不懂。。)主要实现了一个power schedule,慢慢看吧。。
AFL实现的power schedule总是给予高能量(high energy)。给初始化种子大量的能量能够帮助发现更多近邻,这些近邻执行low-frequency路径。举个栗子,fuzz一张有效的图来生成更多有效的图make sense。而且赋值给这些近邻也很make sense。但是执行一段时间后,发现了更多种子,这些种子会执行high-frequency路径,导致若最新的种子是无效图片文件,生成合法图片文件的概率显著降低。
而AFLFAST给high-frequency路径低能量,给low-frequency路径高能量。为了估计fuzzer执行路径 i i i的概率,AFLFAST使用最大似然估计 p ^ i = f ( i ) / n \hat{p}_i=f(i)/n p^i=f(i)/n, f ( i ) f(i) f(i)是fuzz执行 i i i的次数, n n n是fuzz生成所有测试用例经过路径的总数。
同时发现power schedule和search strategies仅仅改变了AFL的效率(单位时间内发现的路径数),并没改变性能(发现路径的数量)。也就是说在发现漏洞的能力上和AFL相同,只是发现的更快。
本文以KLEE这个符号执行器作为baseline进行漏洞检测比较。在KLEE-benchmark上9个error中的6个error,KLEE发现的速度比AFLFAST快,但是AFLFAST找到了5个新的bug。考虑代码覆盖率,AFLFAST比KLEE稍好,AFLFAST平均覆盖82%的代码,KLEE平均覆盖78%。
本文贡献:
- Markov Chain Model. 把基于覆盖的灰盒fuzzing建模成一个有状态空间的Markov chian的系统探索模型
- Power Schedule. 提高较少执行路径执行的可能性
- Search Strategies.
- Comparison to Symbolic Execution.
- Tool. AFLFAST
2 背景
2.1 CGF(Coverage-based Greybox Fuzzing)
cur_location = <COMPILE_TIME_RANDOM>;
shared_mem[cur_location ^ prev_location]++;
prev_location = cur_locationi >> 1;
左移1位是为了区分基本块A和B的顺序的影响。另ID(A) ^ (ID(B)>>1) ≠ (ID(A)>>1) ^ ID(B)
。
Fuzzing stages. AFL对种子输入进行fuzz,随机选择一系列的变异策略并应用到种子输入的随机位置。在确定性变异策略阶段,AFL在输入的每个字节上进行某些变异策略。我们可以认为首次被选到的种子输入有很高的能量。
Binary instrumentation.
Modifications. 本工作只关注CHOOSENEXT实现的搜索策略和ASSIGNENERGY实现的power schedules策略。
2.2 马尔科夫链
一个Markov chain是一个随机变量的序列 { X 0 , X 1 , … , X n } \{X_0, X_1, \dots, X_n\} {X0,X1,…,Xn}, X i X_i Xi表示在时间 i i i时的状态,状态的集合为 S = { 1 , 2 , … , N } , N ∈ N , X i ∈ S S=\{1, 2, \dots, N\}, N\in\mathbb{N}, X_i\in S S={1,2,…,N},N∈N,Xi∈S。马尔科夫链以状态 i i i为初始状态的初始分布为 P ( X 0 = i ) \mathbb{P}(X_0=i) P(X0=i)。
概率矩阵为 P = ( p i j ) \mathbf{P}=(p_{ij}) P=(pij),若 ∣ S ∣ = N |S|=N ∣S∣=N,那么 P \mathbf{P} P为 N × N N\times N N×N的矩阵,每行的和为1。其中 p i j = P ( X t + 1 = j ∣ X t = i ) p_{ij}=\mathbb{P}(X_{t+1}=j|X_t=i) pij=P(Xt+1=j∣Xt=i)。
另
π
\mathbb{\pi}
π表示马尔科夫链的stationary distribution(这是固有分布我终于看懂了哈哈哈哈哈哈哈哈),那么
∀
j
∈
S
\forall j\in S
∀j∈S满足:
0
≤
π
j
≤
1
1
=
∑
i
∈
S
π
i
π
j
=
∑
i
∈
S
π
i
p
i
j
0\leq \pi_j\leq 1 \\ 1 = \sum_{i\in S}\pi_i \\ \pi_j = \sum_{i\in S}\pi_ip_{ij}
0≤πj≤11=i∈S∑πiπj=i∈S∑πipij
3 马尔科夫链模型
3.1 Coverage-based Fuzzing as Markov Chain
Time-in homogeneous model. 假如fuzzer的初始化种子输入 t 0 t_0 t0执行路径0,输入 t i t_i ti执行路径 i i i之后马上探索到路径 i + 1 i+1 i+1。那么转换概率 p i j p_{ij} pij定义为生成的输入执行了路径 j j j若它是由上一个输入 t i t_i ti执行了路径 i i i变异得到的。但是 p i j p_{ij} pij依赖于状态 i i i到达的路径。假如一个输入 t i ′ t_i' ti′也经过了路径 i i i,那么 p i j p_{ij} pij执行路径 j j j的概率会很不一样。也就是说这个马尔科夫链不是时间同构的。
Time-homogeneous model. 给定种子
T
T
T,
S
+
S^+
S+表示由
T
T
T执行到的路径集合,
S
−
⊈
S
+
S^-\nsubseteq S^+
S−⊈S+表示由fuzzer变异得到的输入执行到的路径。那么马尔科夫链的状态集合就是
S
=
S
+
∪
S
−
S = S^+\cup S^-
S=S+∪S−
状态矩阵
P
=
(
p
i
j
)
\mathbf{P}=(p_{ij})
P=(pij)可以表示为:若
i
∈
S
+
i\in S^+
i∈S+,
p
i
j
p_{ij}
pij就是由种子
t
i
t_i
ti变异后的输入执行路径
j
j
j的概率;否则若
i
∈
S
−
i\in S^-
i∈S−,那么
p
i
i
=
1
−
∑
t
j
∈
T
p
j
i
p_{ii}=1-\sum_{t_j\in T}p_{ji}
pii=1−∑tj∈Tpji,其中
p
i
j
=
p
j
i
,
∀
t
j
∈
T
p_{ij}=p_{ji}, \forall\ t_j\in T
pij=pji,∀ tj∈T。这里有俩假设:
- 从种子 t i t_i ti变异得到输入执行路径 j j j与从种子 t j t_j tj执行路径 i i i相似
- i ∈ S − i\in S^- i∈S−没有其他的未发现邻居
根据大数定律,当执行步数 N N N趋向于无穷大时,执行状态 i i i的比例会趋向于固有分布 π i \pi_i πi。
我们称 π \pi π的一个 h i g h − d e n s i t y r e g i o n high-density\ region high−density region是一个路径的近邻 I I I满足 μ i ∈ I ( π i ) > μ t j ∈ T ( π j ) \mu_{i\in I}(\pi_i)\gt\mu_{t_j\in T}(\pi_j) μi∈I(πi)>μtj∈T(πj),其中 μ \mu μ是算术平均。 l o w − d e n s i t y r e g i o n low-density\ region low−density region同理。路径 i i i和 j j j在同个邻居中若 p i j + p j i ≥ ϵ p_{ij}+p_{ji}\geq\epsilon pij+pji≥ϵ,其中 ϵ \epsilon ϵ是一个算术常数。
Sequence of chains. 看自闭了。。
Energy & power schedules. 让所有 s ∈ S + s\in S^+ s∈S+有一个能量。状态 i i i的能量决定了应该由种子 t i t_i ti变异生成的输入数量。一个状态的能量由预定义的power schedule决定。相当于FAIRFUZZ论文给出算法中的 s c o r e score score。
Long tails. 固有分布中有很大数量的very-low-density regions和一小部分数量的very-high-density regions。
由图可知,大概30%的路径在简单的变异后就能执行到(185到280这一段),10%的路径需要生成1k到100k的测试用例后才能执行到(超过平均值的部分,0到30这一段)。也就是说,大多输入执行了少部分high-frequency paths。这些输入常常是不合法的。
Rapid mixing.
Benefits. 马尔科夫模型探索了程序的数值属性的有效近似,如最坏情况,平均执行时间和能量消耗。
3.2 运行实例
3.3 基于覆盖的Fuzzers的挑战
fuzzer通过CHOOSENEXT选择下一个输入
t
∈
T
t\in T
t∈T,然后根据p=ASSIGNENERGY(t)
决定生成变异输入的数量。通常
p
<
M
,
M
∈
N
p\lt M,\ M\in \mathbb{N}
p<M, M∈N,M是变异生成输入的上界,在AFL中,
M
≈
160
k
M\approx 160k
M≈160k。
More Energy Than Needed. 设
i
∈
S
+
i\in S^+
i∈S+,
t
i
t_i
ti是执行状态
i
i
i的种子输入。
X
i
j
X_{ij}
Xij表示
t
i
t_i
ti通过变异发现新路径
j
∈
S
−
j\in S^-
j∈S−所需的变异次数,那么有以下式子:
E
[
X
i
j
]
=
p
i
j
+
(
E
[
X
i
j
+
1
]
)
(
1
−
p
i
j
)
E
[
X
i
j
]
=
1
p
i
j
\mathbb{E}[X_{ij}] = p_{ij}+(\mathbb{E}[X_{ij}+1])(1-p_{ij}) \\ \mathbb{E}[X_{ij}] = \frac{1}{p_{ij}}
E[Xij]=pij+(E[Xij+1])(1−pij)E[Xij]=pij1
Example.
设当 t i t_i ti被选中时,状态 i i i的能量为 p ( i ) = 2 16 = 64 k p(i)=2^{16}=64k p(i)=216=64k。那么当从状态****的输入 t 0 t_0 t0变异了 2 16 2^{16} 216次后,会有几个输入找到状态b***。
Excessive Energy for Hign-Density Regions. 若保留下来的输入执行high-density regions,会有一个趋势–会有太多的能量赋予这些状态。通过定义可知,若马尔科夫链中的状态 i i i的固有分布密度越高,fuzzing t i t_i ti 生成的输入会执行high-frequency paths。
Example. 栗子就不说了,主要抛出了两个挑战,AFL的power schedules:
- 可能赋予过多的能量,超过了需求,给与新且有趣的路径
- 可能赋予太多的能量给了high-density regions,给予low-density regions的能量不够
4 改进灰盒fuzzing
本文提出更有效的基于覆盖的灰盒测试fuzzers发现low-density region未发现的状态,同时分配最少的总能量。
- Search Strategy. fuzzer选择 i ∈ S + i\in S^+ i∈S+,使得 ∃ j ∈ S − \exists j\in S^- ∃j∈S−另 π j \pi_j πjis low且 E [ X i j ] \mathbb{E}[X_{ij}] E[Xij]最小。
- Power Schedule. fuzzer赋值的能量函数 p ( i ) = E [ X i j ] p(i)=\mathbb{E}[X_{ij}] p(i)=E[Xij]给状态 i i i,使得fuzzing发现low-density region时间达到最小
本文提出同构power schedules首先赋值低能量,然后根据选择的次数增加能量。同时,power schedules赋予状态的能量与状态固有分布的密度成反比。若状态 i i i存在于low-density region,fuzzer可以分配更多能量来找这个区域中状态 i i i的neighborhood。
4.1 Power Schedules
s ( i ) s(i) s(i)表示种子 t i t_i ti之前从队列 T T T中选到的次数。 f ( i ) f(i) f(i)表示执行状态 i i i的生成输入的数量。能量函数 p ( i ) p(i) p(i)就是关于 s ( i ) s(i) s(i)和 p ( i ) p(i) p(i)的函数。 f ( i ) / n f(i)/n f(i)/n是最大似然估计。接下来讨论几种power schedules:
exploitation-based constant schedule(EXPLOIT):
p
(
i
)
=
α
(
i
)
p(i)=\alpha(i)
p(i)=α(i),其中
α
(
i
)
\alpha(i)
α(i)是算法中assignEnergy
的实现。AFL根据执行时间,块转换覆盖率,
t
i
t_i
ti的构造时间来计算
α
(
i
)
\alpha(i)
α(i)。
exploration-based constant schedule(EXPLORE):和EXPLOIT差不多 p ( i ) = α ( i ) β p(i)=\frac{\alpha (i)}{\beta} p(i)=βα(i),其中 β > 1 \beta\gt 1 β>1。
Cut-Off Exponential(COE):
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ p(i)=\left\{ \…
其中
μ
=
∑
i
∈
S
+
f
(
i
)
∣
S
+
∣
\mu = \frac{\sum_{i\in S^+}f(i)}{|S^+|}
μ=∣S+∣∑i∈S+f(i),也就是说说
μ
\mu
μ是探索到的所有路径后生成数目数量的均值。
exponential schedule(FAST):
p
(
i
)
=
m
i
n
(
α
(
i
)
β
⋅
2
s
(
i
)
f
(
i
)
,
M
)
p(i)=min(\frac{\alpha (i)}{\beta}\cdot \frac{2^{s(i)}}{f(i)}, M)
p(i)=min(βα(i)⋅f(i)2s(i),M)
s
(
i
)
s(i)
s(i)放在指数部分的直觉理由:本文提出的方法,期望的种子队列
T
T
T本质上需要一个维护一个探索
l
o
w
−
d
e
n
s
i
t
y
r
e
g
i
o
n
low-density region
low−densityregion的输入序列,所以如果
s
(
i
)
s(i)
s(i)越大,直接含义上表示从输入队列中选择
t
i
t_i
ti输入的次数越多,也就是说状态
i
i
i达到的路径数越少,状态
i
i
i是个low-density region,所以放在指数上,
t
i
t_i
ti选取越多,就给它高能量值多变异变异。。。(太强大了,激动地说不出话_(:з」∠)_)
linear schedule(LINEAR):
p
(
i
)
=
m
i
n
(
α
(
i
)
β
⋅
s
(
i
)
f
(
i
)
,
M
)
p(i) = min(\frac{\alpha(i)}{\beta}\cdot\frac{s(i)}{f(i)}, M)
p(i)=min(βα(i)⋅f(i)s(i),M)
quadratic schedule(QUAD):
p
(
i
)
=
m
i
n
(
α
(
i
)
β
⋅
s
(
i
)
2
f
(
i
)
,
M
)
p(i) = min(\frac{\alpha(i)}{\beta}\cdot\frac{s(i)^2}{f(i)}, M)
p(i)=min(βα(i)⋅f(i)s(i)2,M)
4.2 Search Strategies
搜索策略下一个选啥种子,这个也hin重要。这个决策依赖于种子之前被fuzz过的次数和fuzz相同路径的种子的数量决定。本文认为一个好的基于灰盒测试的fuzzer应该给low-frequency paths高优先级。
小 s ( i ) s(i) s(i)优先: s ( i ) s(i) s(i)表示种子 t i t_i ti之前在队列中选到的次数。
小 f ( i ) f(i) f(i)优先: f ( i ) f(i) f(i)表示执行状态为 i i i的生成的输入的数量。
5 评价:VANILLA(香草?理解为原生的) VS BOOSTED CGF
5.1 实现:AFLFAST 1.94b
5.2 漏洞
这里要区别一下漏洞(vulnerability)和bug:https://yq.aliyun.com/ziliao/589078
大致意思就是bug一般指电脑系统的硬件或软件出错,漏洞一般指在软件,硬件,协议的具体实现或系统安全策略上存在缺陷(也就是说漏洞一般和安全相关?)
5.3 一般结果
5.4 Power Schedules比较
结果:AFLFAST中实现的的exponential schedule表现好于其他schedules。*cut-off exponential schedule(coe)*效果略差于AFLFAST。24小时的实验后,所有schedules发现的unique crashes比其他三种(linear,quad,explore)多50%。
5.5 搜索策略的比较
结果:两种策略的结合比各自使用策略效果更好。前12小时其他策略表现相似,24小时后策略1(改变了选favorite的策略)比策略2更有效。策略2(改变了从队列中选取测试输入的顺序)似乎效果不明显。用这个策略和不使用这个策略的AFLFAST效果相似。总之,AFLFAST集成两个策略后找到的unique crashes是原生AFLFAST或集成策略1找到unique crashes数量的2倍。
5.6 结果总结
在GUN binutils v2.26上评价了AFLFAST和几种搜索/调度策略。exponential schedule比其他schedules效果更好。8次6小时的执行中,以工具nm和c++filt作为benchmark,集成了exponential schedule的AFLFAST比AFL找到多找到了1个数量级的unique crashes。在8次24小时的运行中,AFLFAST6个nm中的漏洞,速度是AFL的7倍,并找到了3个AFL没有找到的漏洞。AFLFAST发现nm中的2个bug,速度是AFL的7倍,且找到了一个AFL没有找到的bug。平均来说,AFLFAST找error的速度是AFL的17倍,并且7个error是AFL没找到的。
6 基于符号执行的白盒fuzzing VS 基于覆盖的灰盒fuzzing
结论:在KLEE的benchmark bugs上进行评价比较
- 在效率上,KLEE比AFLFAST好
- 在效果上,1个小时的timeout内,AFLFAST的效果比KLEE好
为了检测漏洞,KLEE需要基于约束的漏洞检测机制和假设约束编码的完整性和环境建模,而AFLFAST仅仅需要执行程序报告任何crashes。
考虑代码覆盖率,AFLFAST比KLEE稍好。当然,结合起来使用效果更好,使用它们各自的强项来减弱对方的弱点。
7 在AFL 2.33B上部署
8 相关工作
9 结论
符号执行器的扩展性不如黑盒或灰盒的fuzzers。
本文提出一个方法改进AFL工具产生crash的效率。AFLFAST在单位时间内可以发现比AFL更多的unique crashes而且可以发现AFL不能发现的漏洞,其他的漏洞AFLFAST发现的比AFL早。
更重要的是,本文提供了通过把CGF建模为Markov chain探索状态空间的尝试。本文的方法会尝试隐藏在low-density region上更多的状态和在high-density region上生成更少的输入。
一篇paper看两天。。