[关闭]
@SmashStack 2017-03-21T07:12:35.000000Z 字数 3719 阅读 46

Packed, Printable, and Polymorphic Return-Oriented Programming

Binary


这篇论文发表在 RAID 2011 中,介绍了通过两层解码的方式对传统 ROP 流量进行打包, 生成简短多变并且完全由可打印字符串构成的新型攻击流量。

作者
- Kangjie Lu @ Singapore Management University
- Dabi Zou @ Singapore Management University
- Weiping Wen @ Peking University
- Debin Gao @ Singapore Management University

1. 背景介绍

ROP (Return-Oriented Programming) 是一种通过 Stack Overflow 对函数返回地址进行劫持的攻击方式。攻击者控制堆栈调用以劫持程序控制流并执行针对性的机器语言指令序列(称为Gadgets),一般而言,每一段 Gadget 以 Ret 指令结尾,但是在某些特殊情况下,也可以以 Jump 或者 Call 指令结尾。 在过去的研究中已经证明了 ROP 足够让攻击者执行任意代码。 同时因为 ROP 不需要代码注入,也使的现在很多安全防御措施(如 NX 可写段不可执行)失去的效用。

但是,ROP 的攻击效用是否真的如传闻中说的那么神通广大呢?事实上,在现实生活中,我们的攻击流量面领着这样三个甚至更多的问题:静态分析流量过滤特征分析。在接下来的部分中,作者着重讨论了:

并由此引出了他们所提出的 两层编码 方式。

2. 实现

2.1 初步的想法

介于很多实用的 Gadget 的地址都不全是可打印字符 [0x20, 0x7e],作者首先提出了一种单层编码方式。这种编码方式通过加法实现任意地址的 Gadget,逻辑简单清晰,美中不足在于其会导致攻击流量冗长,使得适用范围大大受限。

这种编码方式的核心思想在于通过加法运算在 fake stack 上进行真正的 ROP 栈布局,随后将 esp 指向 fake stack。具体流程图如下:

Created with Raphaël 2.1.2StartFake an operation on addr: Pop op1 into eax Pop op2 into ecx Add eax, ecx mov [edx], eax add edx, 4Finish fake stack?Stack Pivotyesno


接下来,我们通过例子来了解这个编码方式。下图是 subroutine 的具体实现。

image_1bb9bjq711puk40h1j8l9uvl1t9.png-70kB

首先在 Gadget 1 中,程序将 fake stack 的起始地址 addr 放入 edx,随后 Gadget 2Gadget 3 分别将 0x2d30466c、0x50294030 放入到 ecx 和 eax 中。 Gadget 4 实现了两个寄存器的加法并存入 eax。在 Gadget 5 中, eax 与 edx 的值进行了交换,而随后的 Gadget 6 则将加法得出的不可打印的 Gadget Address (0x7d59869c) 放入 eax+0x1c 的内存上。 Gadget 7 实现了 eax 加 4,而 Gadget 8 就是 Gadget 5,同样实现了两个寄存器的交换。

如此反复,将 fake stack 上填满了我们需要的不可打印的 Gadget Address 以后, 通过 xchg esp, eax 实现 Stack Pivot。最后进行正常的 ROP。

然而,这样的方法存在着两个致命的缺点:

于是就有了接下来的考虑。

2.2 改进方式

刚才我们已经提到了,单层编码导致 Payload 冗长,如果我们能够将数据与执行的 Gadget 分离,真正实现以上流程图的循环方式,那么我们生成的 Payload 的长度将大大减小。

可不幸的是,能够实现 ROP 循环的 Conditional Jump 的 Gadget 很稀少特殊,更难被一个 Printable Address 表示。为此,作者们设计了一种 两层编码方式 (Two-layer packer)。

2.2.1 总体设计

image_1bbab1aqajlr9poftpi81f3n9.png-52.4kB

如上图所示,两层编码需要经过两次解码获得原始的 ROP Payload。在第一层编码中, dec1 通过类似于单层编码的方式对 enc1 (dec2) 进行编码,使其成为能够构成 Conditional Jump 的循环 ROP;而在第二层编码中, dec2 通过循环的方式将 enc2 编码,获得原始的 ROP Payload。 因为dec2 是通过编码生成的,所以它不需要是 Printable 的,因而解决了 Conditional Jump 的 Gadget 无法被使用的问题。

Implementation of dec1

dec1 分为 InitializingDecoding enc1 (dec2) 两个部分

2.2.2 Implementation of dec2

类似于 dec1, dec2 也分为 InitializingDecoding enc1 (dec2) 两个部分。但是值得一提的是,介于 dec2 使用了循环的流程,它要更加复杂一些

实验

作者对 Windows 7 & Windows XP 上对 Winamp v5.572RM Downloader v3.1.3 的 ROP 攻击流量进行了 Pack,得到了如下的试验结果

image_1bbaj2pd11qmg4iqqm18os1mse3a.png-225kB

可以看见,这个编码流程还是很具有可行的。

总结

作者为我们展现了通过对 ROP 攻击流量进行打包,从而绕过多种限制和查杀手段。从整体上来看,作者提出的两层解码可以有效地绕过可见字符串的过滤,但是即使这样,使用条件也受到了 ASLR 的限制(用于 dec1 的固定 Gadget 会不断变化,导致部分 Gadget Address 不可打印)。另一方面,特征明显的问题却没有被多样性所解决,因为 dec1 部分的 Gadget Address 是不会变化的,这样敏感的头部信息可以作为有效的特征码。

但不得不说,对流量混淆方面该编码方式有着不错的成效。从另外一个方面提出了对 ROP 流量特征查杀也有着先进之处。

PRO:

CON:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注