Angr AEG:堆溢出之Exploit自动生成

本文主要介绍如何基于 Angr 进行漏洞挖掘,并自动生成 Exploit。对于 Exploit 的自动生成问题,也被称为 AEG(Automatic Exploit Generation),其中包含漏洞挖掘、Crash分析、约束条件构造、约束求解、Exploit 生成等环节,下文通过符号执行实现对二进制程序的自动化漏洞挖掘及利用,并展示完整的 AEG 过程。文中所分析的漏洞程序为 Insomni`Hack 2016 题目之一。下载地址,其中包含漏洞程序源码 demobin.c、编译后的可执行程序 demobin 以及 Angr 脚本 solver.py。

0x00 漏洞原理

1、源码分析

首先查看源码 demo_bin.c,存在一处堆溢出漏洞,关键点如下:

1) component_name[128] 大于 component->name[32];

2) initializacomponent( char *cmpnaem) 函数中,在赋值时未检查缓冲区大小;

3) 调用 do_something() 时,产生 Crash。

2、GDB 调试

分析过程序源码后,利用 GDB 动态调试 demobin,以触发 Crash。首先通过 r2 查看 initializacomponent( char *cmp_naem) 所对应的汇编代码。

分析后可知,结构体 component 大小为 36 字节,其中 component->name[32] 占用 32 字节,随后 4 字节为函数指针,因此构造 PoC 为 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB"。

利用 GDB 加载 demobin 并输入 PoC,在 initializacomponent( char *cmp_naem) 函数中,调用 malloc() 之后设置断点。查看此时 malloc() 所分配的内存情况。

继续调试,单步至 initializacomponent( char *cmpnaem) 函数返回,查看此时内存情况。可见 cmp 在堆上的地址为 “0x804b410”,而 cmp->do_something(1) 所对应的地址 “0x804b430” 此刻已被 “\x42\x42\x42\x42” 所覆盖,如下图所示。

当程序执行至 cmp->do_something(1) 时触发 Segmentation fault,此时 EIP 为 “\x42\x42\x42\x42”,表明程序的控制流已被劫持。

通过以上简要分析可知,demo_bin 中存在堆溢出漏洞,可导致控制流劫持。在此基础上,下文主要介绍如何通过 Angr 实现对该漏洞的自动化挖掘以及利用。

0x01 Angr AEG

完整的 AEG 过程,在逻辑上大致可分为以下几个环节:

1) 漏洞挖掘,Angr 主要采用带有前置约束及路径选择策略的符号执行; 2) 崩溃现场分析:EIP 状态、内存布局; 3) 约束条件构造; 4) 约束求解,Exploit 生成;

1、漏洞挖掘

在本例中,主要是针对控制流劫持漏洞的挖掘。利用符号执行检测控制流劫持,关键在于 EIP,若 EIP 完全被符号变量所覆盖,则代表着控制流可以被劫持,此时 Angr 会抛出 unconstrained 状态。

solve.py 中 65 ~ 84 行,通过搜索二进制程序的状态空间以实现漏洞挖掘。由于 demo_bin.c 中的漏洞逻辑较为简单,因此在挖掘过程中并未加入复杂的前置约束以缓解路径爆炸,也未采用额外的路径搜索策略,仅使用 SimulationManager 的 step() 方法,循环执行,直到出现 unconstrained 状态。

注意,在设置 SimulationManager() 时,save_unconstrained 必须设置为 True。

2、崩溃现场分析

脚本运行不久后,便会触发 unconstrained 状态,此时需要对崩溃现场进行分析,以判定 unconstrained 状态的可利用性。

1) EIP 可控性分析

solve.py 中使用 fullysymbolic() 方法检查 EIP 中符号变量的数量。其中 state.arch.bits 代表系统字长(The number of bits in a word),state.solver.symbolic() 用以判断输入数据是否为符号变量,该方法在 ./angr/stateplugins/solver.py 中实现:

当 EIP 完全被符号变量覆盖时,代表控制流已被劫持,此时堆及 EIP 状态如下:

在实际调试过程中,会出现多次 unconstrained 状态,但前若干次符号变量均未能完全覆盖 EIP,因此并不能利用。最终 EIP 被符号变量完全覆盖时,其内容如下:

2)内存布局分析

在触发控制流劫持后,需要分析当前状态下内存中符号变量的分布情况。solve.py 中的 findsymbolicbuffer() 实现相关功能,主要包括查找符号输入、追踪符号变量两个部分。

(1) 查找符号输入

Angr 在处理 scanf 的输入数据时,采用 streams 模式。默认情况下,stdin、stdout、stderr 均采用该模式。state.posix.stdin 为输入程序的全部符号变量,调试结果如下:

(2) 追踪符号变量

通过 state.solver.getvariables() 追踪内存中的符号变量。该函数位于 ./angr/stateplugins/solver.py 中,返回结果如下:

state.memory.addrsforname() 返回符号变量对应的地址,结果如下:

至此,已完成漏洞挖掘与崩溃现场的分析,后续需要结合 Exploit 的方式(ret2text、ret2syscall、ROP 等),构造完整的约束条件,并求解。

3、约束条件构造

对于 Exploit 自动生成问题来说,其关键是构造合适的约束条件,并利用 SMT(Satisfiability Modulo Theories) 约束求解器求解,若约束可解,则生成成功,否则生成失败。其中包括路径约束、EIP 约束、shellcode 约束等。

本例中,Exploit 方式采用 ret2text 且不考虑安全机制,因此仅需找到一片足以放置 shellcode 的连续空间即可。检查空间连续性的函数如下:

solve.py 中的 shellcode 大小为 22 字节,最终构造约束条件如下:

4、约束求解

通过 ep.satisfiable() 对约束条件 extraconstraints=(memory == scbvv,ep.regs.pc == buf_addr) 的可解性进行判断。最终判定为约束可解,并生成 Exploit:

0x02 小结

本文以简单的堆溢出为例,展示了如何利用 Angr 自动生成 Exploit。与此同时,也展示了 AEG 的完整过程。文中并未涉及复杂漏洞的自动利用及符号执行所面临的路径爆炸、路径选择、约束求解等问题。由于笔者接触 Angr 时间不久,文中难免存在理解不当之处,望各位大佬批评指正(Orz...)。

*本文原创作者:xiaohan0x00,本文属FreeBuf原创奖励计划,未经许可禁止转载

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2019-02-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区