PolyAEG
相对于前人的工作,一个是加强了污点分析的能力,通过提供后向遍历分析来踢狗各种分析。特别是利用生成,可以确保我们能够精确定位到控制流的劫持点,获得路径约束和数据流依赖。
其二是通过利用trampoline指令链多样化跳转方式,从而提供多样化的利用方式,提高了可利用性。
1. Abstract
问题:从攻击者的角度生成漏洞是对已知漏洞进行严重性分析的一种有效方法。然而,即使使用程序二进制文件和已知的异常输入导致程序崩溃,也会产生一个利用漏洞的开放问题,更不用说多次利用了。
方法:提出PolyAEG系统,对一个脆弱性程序使用一个相应的异常输入自动产生多态利用。充分利用不同的trampoline指令来劫持控制流,并将其重定向到执行上下文中的恶意代码。
结果:成功对8个程序生成了控制流劫持利用
2. Introduction
实现PolyAEG,需要解决以下几个问题:
- 哪些输入字节需要修改
- 应该赋予什么值
- 基于异常输入,我们如何多样化产生利用
在污染分析期间,PolyAEG检测所有可能的劫持点,概括当前执行路径的约束,并识别所有用户控制的内存区域。当一个劫持点被检测到,PolyAEG利用trampoline指令和shellcode,然后将他们放到用户可以控制的内存区域,确保执行流到达shellcode。
PolyAEG可以清楚地识别程序输入和容纳元素之间的数据依赖关系,因此PolyAEG可以找到所有相关的输入字节。它们应该被修改为利用生成。(解决问题1)
为了高效利用,修改的值应该满足路径约束和数据依赖。PolyAEG分别求解这些值,用他们来构造新的输入。(问题2)
多样化不同trampoline指令和shellcode之间的组合,从而产生多态利用。
3. Overview of PolyAEG
Phase1:动态信息提取,我们分析了污染的传播过程,以检测控制流的劫持点,并提取污染的内存区域来存储所使用的trampoline指令和壳代码。
Phase2:约束生成,该阶段的目标是产生约束路径,确保在程序运行时可利用的输入能够到达劫持点。路径约束的产生基于阶段1的污点执行信息。它们由一组约束公式表示,输入数据作为要检查的变量。
Phase3:利用生成,利用trampoline指令来构造trampoline指令链,从而重定向程序执行到shellcode。将跳转链和shellcode放到被污染的内存区域。根据前两阶段确定的指定数据依赖和路径约束,通过修改相关的输入字节,最终生成一个利用点。
4. Evaluation
PolyAEG基于QEMU实现,通过修改QEMU来支持进程标识、动态插装、系统调用拦截和动态污点分析。使用Z3产生路径约束并查询满足的结果。从加载到脆弱进程的模块中搜索trampoline地址。根据Intel assembly语法生成trampoline操作码。从Metasploit选择Shellcode。基于程序输入生成包含trampoline和shellcode的多样化可利用数据,最终将他们写入文件形成最终的数据。
运行时的上下文信息可能不同,但是trampoline和tmg(taint memory garget)地址之间的偏移量不变。从而保证了执行流重定向的正确性。
4.1 有效性
当Freefloat Ftp处理畸形的远程用户指令会crash。在实验中,发送用户指令(1024"A"),将会造成crash。PolyAEG执行动态污点分析,可以看到0x00402ebb
处的ret 0x8
会被执行,污点数据将被加载到EIP
作为返回值。
Table2显示了候选trampoline指令,CandL表示tmg的尺寸大于shellcode,Cands表示tmg的尺寸小于shellcode。call esp
和call [ebp+0x14]
可以用于In,因为他们对应的tmg足够大, call edi
call esp
可以用于I0,因为他们可以在进程地址空间的代码段获取。通过分析不同的可能性,构造TrampChain,可以获得4个高效的TrampChains,如Table3所示。#exploit表示产生利用的计数。
选择第4个TrampChain来构造执行流重定向。利用详情如table4所示。
TrampChains的不同模式和shellcode调节的多种选择有助于多态利用的生成。请注意,shellcode的长度决定了最后使用的Trampoline构造TrampChain。因此,如果使用另一个具有不同长度的shell代码,我们可以获得另一组不同的trampchain。此外,还有更多可利用的漏洞。
4.2 多态
4.3 性能开销
开销主要存在于污点分析和利用生成。前者主要取决于受污染的指令数,后者主要取决于约束求解。
Fig8和Fig9下图比较了几个程序中上述两部分的开销规模。
内存开销:通过内存使用率来描述内存开销
作者认为PolyAEG作为一个离线的利用生成系统,有合理的时间和内存开销。但目前也没有对PolyAEG做一些优化。之后可以通过对约束降重、简化符号变量来优化。
5 Limit
首先,PolyAEG产生控制流劫持利用。它在绕过ASLR和DEP方面做了有限的努力。其次,部分地控制被攻击的返回地址或函数指针是利用的障碍。PolyAEG只能在特定的条件下产生exploit。第三,shellcode是完全存储的。当shellcode被分割成几个单元并放置到不同的污染区域时,我们不会考虑这种情况。此外,由于整个系统的符号执行费用高昂,PolyAEG无法识别所有可能的路径
利用非控制流劫持漏洞仍然是一个有待解决的问题
6 Method
6.1 Dynamic Information Extraction
通过构造iTPG(instruction-level Taint Propagation Graph)andGTSR(Global Taint State Record)来加强污点分析方法。
Dytan: a generic dynamic taint analysis framework.
Dynamic taint analysis for automatic detection, analysis, and signature generation of exploits on commodity software
在程序运行期间iTPG记录污点传播信息。如Fig2中,灰色的节点表示来自输入数据对应的污点源内存。白色的节点表示被污染的指令。边表示污染数据之间的依赖关系。
GTSR记录污点状态,包含内存字节,通用寄存器和位标记EFLAGS。通过一个三元组进行表示<TaintId, TaintStat, iTNode>,TaintId表示每个字节或者位的ID,TaintStat表示是否被污染,iTNode表示一个指针,指向受污染的指令。一个32位寄存器由GTSR中的4个字节指定。
iTPG和GTSR反应了污点传播时的上下文信息。当一条受污染的指令 ti修改GTSR中记录的受污染字节时,加入一个新的iTPG节点。找到修改被污染的字节的最后一条指令,将其指向 ti,然后将 ti指向 ti‘。
根据iTPG和GTSR,可以确定受污染字节的输入相关字节和他们之间的数据依赖。如Figure2所示,假如一个受污染的字节tb在GTSR中有相应条目,iTNode指向一个iTPG节点,表示最后一次修改tb的受污染指令。从该后向遍历iTPG直到source节点,我们可以得到由标记的受污染指令组成的路径。从此,我们可以确定tb相关的输入字节ini,。。。,inj和他们之间的数据依赖value(tb) = f(value(ini),。。。,value(inj)),f可以通过路径中受污染的指令进行推导。
在动态污点分析期间,检测劫持点,通过检查受污染的数据是否被用于间接迁移(ret,jmp,call)。当一个劫持点被检测到,我们确定受污染内存区的布局和路径约束。一个受污染的内存区是由受污染的字节组成。我们用tmg(tainted memory garget)
来表示,可以用<start, end>
来描述,其中start是起始地址,end是结束地址。可以从GTSR中提取所有的tmg,表示为TMG。通过分析由iTPG指明的执行路径来确定路径约束。
6.2 Constraint Generation
通过修改输入数值来生成利用,生成时需要满足特定的谓词约束,这些约束保证程序能够执行到姐吃点,特别是当输入包含checksum域时。为了保证劫持点是可达的,我们确定到达劫持点时与输入相关的分支,并标准化这些约束(反应了分支的结果)。
将输入派生的分支记为tainted branches
。在每个tainted branches
,我们确认影响分支结果的输入字节,标准化相应的约束。一个tainted branches
指令对应iTPG中的一个节点。沿着iTPG反向遍历到污染源,我们可以获得相关的输入字节和一个iTPG节点序列,表示相关的污染指令路径。这些路径可以用于对tainted branches
标准化约束。本文使用Z3来实现。首先,我们赋予输入字节不同的符号变量,然后进行动态符号执行。在分支指令中,由于相应的位在EFLAGS标明,我们根据约束公式的值生成SMT格式的约束公式。
在tainted branches
生成的路径约束可以保证劫持点可达。然而,可能存在如下形式的副作用。
if(strcmp(taintstr, "http"))
goto loc_1;
else
goto loc_2;
loc_1:
// do sth causing the return address overwriteen
// ...
return; // hijacking point
loc_2:
return;
对于约束会产生
t
a
i
n
t
[
0
]
!
=
′
h
′
∣
∣
t
a
i
n
t
[
1
]
!
=
′
t
′
∣
∣
t
a
i
n
t
[
2
]
!
=
′
t
′
∣
∣
t
a
i
n
t
[
3
]
!
=
′
p
′
taint[0]!='h' || taint[1]!='t' || taint[2]!='t' || taint[3]!='p'
taint[0]!=′h′∣∣taint[1]!=′t′∣∣taint[2]!=′t′∣∣taint[3]!=′p′的结果,消耗太大。为了解决这个问题,将生成的约束分为两类,其一是tainted branches
,其二是tainted lirary call
,当受污染的库函数被调用,暂停tainted branches
约束生成,同时确定返回地址和调用参数,我们根据函数的语义和返回结果,生成约束公式,其中包含被污染的参数作为符号变量。在那之后,我们继续生成受污染的分支约束。受污染的内存库中一般是字符串比较函数或者内存比较函数,例如strcmp, strncmp, memcpy
等。他们在脆弱程序中经常用到,并且能够影响能否到达劫持点。
6.3 Exploit Generation
利用生成位于劫持点检测之后。根据上下文信息构造由trampoline指令组成的trampoline指令链。shellcode和trampoline指令链应该放在受污染的内存区域。
为了生成利用,首先为shellcode和trampoline指令链找到相关的输入字节。然后将他们修改为合适的字节,确保目标利用过程。当脆弱程序运行时,生成的利用,1)所利用的trampoline指令和shellcode可以出现在预期的位置,2)控制流可以在劫持点被接管,3)挨个执行trampoline指令,直到shell代码最终执行。
6.3.1 构造Trampoline指令链
利用以下三种类型的trampoline指令:
{call/jmp register}操作数为寄存器,8个通用寄存器可以作为call/jmp的操作数,因此可以得到16中trampoline指令。
{call/jmp [register+offset]}间接内存访问,由寄存器和偏移量组成。本文中,偏移量范围被设置为-256到+256,那么最终可以构造8192中trampoline指令。
{successive instructions sequence}该类型的trampoline是代码段中的连续指令序列,被加载到进程地址空间。在执行期间就像执行一条指令,所以我们将他们视为一条trampoline指令。本文中只考虑pop,pop,ret
类型的指令。
给定一个trampoline指令 I I I,我们可以准确计算出在当前上下文中该指令的目标地址,记为 I . t a r g e t I.target I.target。那么 I . t a r g e t I.target I.target必须在tmg中( t m g . s t a r t < = I . t a r g e t < = t m g . e n d tmg.start <= I.target <= tmg.end tmg.start<=I.target<=tmg.end)。并将该 t m g tmg tmg记为 I . t m g I.tmg I.tmg。最终可以获得一系列trampoline指令 C a n d = { I ∣ t m g . s t a r t < = I . t a r g e t < = t m g . e n d , t m g ∈ T M G } Cand=\{I|tmg.start<=I.target<=tmg.end,tmg \in TMG\} Cand={I∣tmg.start<=I.target<=tmg.end,tmg∈TMG}。
将trampoline指令链记为 T r a m p C h a i n = I 0 , I 1 . . . , I n TrampChain=I_0,I_1 ..., I_n TrampChain=I0,I1...,In,且 1 < = n < = ∣ C a n d ∣ , I 0 , I 1 , . . . , I n ∈ C a n d 1<= n <= |Cand|, I_0,I_1,...,I_n \in Cand 1<=n<=∣Cand∣,I0,I1,...,In∈Cand。一个成功的重定向执行可以用Figure3来描述。其中 I . a d d r I.addr I.addr表示进程空间中 I I I的内存地址, I . c o d e I.code I.code表示 I I I的操作码。我们可以获得如下特性:
- I 0 . a d d r I_0 .addr I0.addr在间接控制流迁移时被受污染数据所取代(例如受污染的返回地址或者函数指针)
- I j + 1 . c o d e I_{j+1}.code Ij+1.code被放在 I j . t a r g e t I_j .target Ij.target中,0<=j<n;
- shellcode s放在 I n . t a r g e t I_n .target In.target中
根据上述特性,从 C a n d Cand Cand中选取合适的trampoline指令组成 T r a m p C h a i n s TrampChains TrampChains。之后选择 T r a m p C h a i n s TrampChains TrampChains中符合如下标准的:
- I 0 . a d d r I_0 .addr I0.addr能够在进程空间中的非随机地址中被发现
- l e n ( I j + 1 . c o d e ) < = I j . t m g . e n d − I j . t a r g e t , f o r 1 < j < n ; len(I_{j+1}.code) <= I_j.tmg.end - I_j.target, for 1 < j < n; len(Ij+1.code)<=Ij.tmg.end−Ij.target,for1<j<n;
- I n . t m g . e n d − I n . t m g . s a r t > = l e n ( s ) ; I_n.tmg.end - I_n.tmg.sart >= len(s); In.tmg.end−In.tmg.sart>=len(s);
放置 T r a m p C h a i n TrampChain TrampChain可能会存在地址冲突。主要考虑两钟地址冲突:
- I j I_j Ij和 I k I_k Ik被放在相同的tmg中,且 I j I_j Ij被 I k I_k Ik覆盖, 0 < j < k < n 0<j<k<n 0<j<k<n;
- I j I_j Ij被单call的trampoline指令 I m I_m Im执行的返回地址覆盖。
通过如下的方式进行解决:
针对第一种情况,有两种不同的情形:
其一是
I
k
I_k
Ik在
I
j
I_j
Ij前面,即
I
k
−
1
.
t
a
r
g
e
t
<
I
j
−
1
.
t
a
r
g
e
t
<
(
I
k
−
1
.
t
a
r
g
e
t
+
l
e
n
(
I
k
.
c
o
d
e
)
)
I_{k-1}.target < I_{j-1}.target < (I_{k-1}.target+len(I_k.code))
Ik−1.target<Ij−1.target<(Ik−1.target+len(Ik.code)),此时需要在tmg的其他地方放这段代码,例如Fig4.a图中
I
k
−
1
.
t
a
r
g
e
t
′
I_{k-1}.target'
Ik−1.target′处。并添加jmp
指令,跳转到该地址。
其二是 I j I_j Ij在 I k I_k Ik前面,此时无法添加跳转代码,在这种情况下是无法解决的,无法生成有效利用。
针对第二种情况, 0 < j < = n 0<j<=n 0<j<=n,
当 j > m > = 0 j>m>=0 j>m>=0, I m I_m Im的返回地址会改变 I j I_j Ij的内容,导致无法执行。这种情况无法解决,丢弃该情况下的trampoline指令链;
当 j < m < = n j<m<=n j<m<=n,此时由于 I j I_j Ij在 I m I_m Im之前完成了执行,所以控制流不受影响。
6.3.2 Exploit Construction
完成劫持点检测,并将trampoline指令链和shellcode放到了没有地址冲突的受污染内存区,那么最终就可以通过修改相关输入字节来构造利用。
目前我们的方法也可以在地址中部分字节被控制的情况下使用。传统情况下这意味着不充分的控制,以至于无法成功利用。我们枚举地址中所有可能的取值,如果存在一个可以获得的trampoline指令地址,我们就能够劫持程序执行,并实施之后的重定向。当前的方法在没有随机化的模块中可以较好的实现。
参考文献
Brumley:Automatic patch-based exploit generation is possible: Techniques and implications
比较受害程序和他们的补丁来生成利用点。
Lin:Convicting exploitable software vulnerabilities: An efficient input provenance based approach.
动态利用生成方法,通过改变一组与执行脆弱代码位置相关的输入值。只会造成程序崩溃,无法证明这个脆弱性是否能够执行恶意代码。
Avgerinos:Aeg: Automatic exploit generation
提出第一个产生包含恶意代码的利用点的系统,通过源码分析和位置符号执行的方法。然而,这种方法不能用于闭源程序。