Data riented rogram 论文阅读与个人理解

0x00 简介

​ Data-Oriented-Program (DOP) 由 Hong Hu 等人在于2016 年在论文《Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks》中提出。其核心思路是解决在控制流完整性保护 (CFI)、代码指针保护 (CCFI, CPI)、数据执行保护(DEP)、 以及时间地址随机化 (TASR) 等技术下,Shellcode 注入 和 ROP 等技术失效的问题。

​ 程序的运行时可以分为控制流和数据流,此前一般的攻击方式都是针对控制流的,因此防御策略的更新也大多是针对控制流的,例如对控制流完整性的保护。但是针对数据流攻击的保护并不算强,而且数据流的安全问题同样具有强大的破坏力。例如私钥的泄露,用户提权等问题都是破坏数据流导致的。

​ 论文作者提出了 DOP 的攻击方式,证明针对数据流同样由类似 ROP 的攻击方式,并且与 ROP 一样是受限图灵完全的。(受限主要是因为 DOP 和 ROP 都依赖 gadget,在 gadget 数量足够模拟所有图灵机指令的时候,ROP 和 DOP 都是图灵完全的)

0x01 利用方式

​ 作者直接给了例子可以充分展现 DOP 的能力。

​ 例程一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct server{
int *cur_max, total, typ;
} *srv;
int connect_limit = MAXCONN;
int *size, *type;
char buf[MAXLEN];
size = &buf[8];
type = &buf[12];
// ...
while(connect_limit--) {
readData(sockfd, buf); // vuln with bof
if (*type == NONE) break;
if (*type == STREAM)
*size = *(srv->cur_max);
else {
srv->typ = *typ;
srv->total += *size;
}
// ...
}

​ 在例程一中,readData 函数有缓冲区溢出,我们可以覆盖到 size, type, connection_limit, srv。看到这,应该已经知道这个 DOP 撕下表皮下的内涵是什么了。不急,接着看。

​ 例程二:

1
2
3
4
5
6
7
8
struct Obj{
struct Obj *next;
unsigned int prop;
}
void updateList(struct Obj *list, int addend) {
for (; list != NULL; list = list->next)
list->prop += addend;
}

​ 例程二是一个链表更新的操作。作者希望用例程一来模拟例程二。具体怎么模拟的,可以看作者在 USENIX 的演讲视频。视频链接

​ 整个过程并不复杂,通过 while 循环控制 size, type, srv 很容易就能实现这个效果。唯一一点没有提及的是 size 和 type 都是地址,我们并不知道也没有泄露栈地址,但是这不是主要内容。

​ 整篇论文最主要的精华是思想,论文将整个数据流劫持的过程抽象出来,并提出了一套图灵完全的语法描述,来说明 DOP 的图灵完整性。

0x02 MiniDOP

​ 先列一下 MiniDOP 的操作,以及和 C 语言的对比。MiniDOP 的 “操作指令” 其实就是 Gadgets,但是我们关注的重点不是在“执行”(控制流)上,而是在”数据”(数据流)上,换句话说,Gadget其实是内存中存放的数据,而不是类似 ROP 的执行语句。(感觉在讲哲学)

语义 C语言 DOP Gadgets
算术/逻辑运算 a op b *p op *q
赋值 a = b *p = *q
加载 a = *b *p = **q
储存 *a = b **p = *q
跳转 goto L vpc = &input
条件跳转 if a goto L vpc = &input if *p

​ 在 DOP 的图灵机模型中,寄存器不是真实的 CPU 寄存器,而是内存模拟的寄存器。例如在例程一中,&(srv->typ) 就是一个寄存器,这块内存区域中能够存放数据,就有寄存的功能了, 同理 type 也是一个寄存器。所以 srv->typ = *typ 就是一个在 DOP 语义下的赋值操作。

​ 如果把 srv->typ 看作一个整体,那么 srv->typ = *typ 是以一个赋值操作。但是如果拆开来看,这其实是一个储存操作,对应**p = *q,这一点也不难理解。

​ 因此,如果有足够的 DOP Gadgets 就能保证做到对程序任意可读地址的提取(储存操作),以及对任意可写地址的改写(加载操作),以及最最重要的,图灵完全。

​ 但是完成 DOP 不止需要 Gadgets,还需要一个 while 1 循环,以及一个 Dispatcher。这个 Dispatcher 在例程一中就是 if 判断语句,Dispatcher 需要能够控制执行哪种操作(运算、赋值、加载、存储等)。看到这我相信 pwn 手都能够会心一笑,的确,这不就是菜单堆吗。

0x03 菜单堆与DOP

​ 其实在 CTF 中,用户态堆利用技术(堆风水)就是 DOP 的一种情况,在堆布局的过程中我们不能直接控制程序流的执行,而是通过对数据(控制数据)的操作来获得地址读和地址写的权限,最后一步才是劫持控制流。这篇文章是16年写的,那个时候堆利用才刚刚兴起不久。这篇文章从学术的观点解释了数据流在漏洞利用的重要性,并且以一个新奇的角度来说明数据流劫持也可以达到图灵完全的效果,从理论指导和利用哲学的角度来看,是有巨大贡献的。