# wild copy型漏洞的利用

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-03-24T06:11:00Z
Canonical: https://wepostx.com/topics/762

> 乌云历史资料归档
>
> **原始作者：** cssembly
> **原始编号：** superkieran-wooyundrops:515
> **原始发布时间：** 2015-03-24 14:11
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

# 0x00 前言

这是Project Zero上的文章，原文为《Taming the wild copy: Parallel Thread Corruption》
链接: http://googleprojectzero.blogspot.com/2015/03/taming-wild-copy-parallel-thread.html
2002年，Apache Web服务器中发现并修复了一个非常有趣的漏洞。服务器在处理分块传输编码时存在缺陷，漏洞将导致memcpy()调用的长度为负值，且目标缓冲区在堆栈上。当然，很快许多人就宣布这一漏洞是不可利用的，只能造成拒绝服务。毕竟memcpy()函数调用时长度字段采用负值，将导致巨大的内存空间拷贝，肯定会命中一个未映射的内存页导致进程终止。由于FreeBSD和其他BSD衍生版本在memcpy()实现上的一些特性，出现了可用的远程代码执行漏洞利用程序，让人感到了惊喜！ GIAC文章 [LINK](http://www.giac.org/paper/gcih/361/port-80-apache-http-daemon-exploit/103818) 记录了漏洞利用代码，并分析了它是如何工作的（GIAC文章的附录D中）。
为了清楚的描述这篇文章的目的，我们将wild copy定义为满足下面两个条件的内存拷贝：

- 攻击者对内存拷贝的长度拥有有限的控制权。

- 内存拷贝的长度总是能够导致对未映射页的访问。

在许多其他有趣的情况下，wild copy型漏洞最终可以被利用：

- 针对Windows结构化异常处理程序（SEH）的攻击。

- 不止BSD衍生版本上存在类似memcpy()特性的问题。 2011年，在Ubuntu上eglibc漏洞 [LINK](http://sebug.net/paper/Exploits-Archives/2012-exploits/1208-exploits/eglibc-exec.txt) 是由于对memcpy()函数的调用采用了任意的长度字段。memset()变体漏洞 [LINK](https://code.google.com/p/chromium/issues/detail?id=91665) 通过Chromium漏洞奖励计划被披露。

- Java运行时环境中安装了非常复杂的崩溃处理程序，当wild copy不可避免地触发访问异常时被调用。在堆状态任意损坏的情况下，如果崩溃处理程序不够健壮，它自身可能会崩溃，导致漏洞更加可控和可利用。

上述所有情况都有一个共同点：wild copy漏洞可利用性由次要问题（secondary issue）或特性导致。
是否有可能利用wild copy漏洞，而不依赖于次要问题？这是Project Zero要解决的一个有趣问题 -- 当我们探索漏洞利用时，我们可以更加了解问题，或者推动利用技术的发展。今天，我们要探索64位Linux上，Chrome的Flash插件的wild copy漏洞。
现在是时候引入[问题122](https://code.google.com/p/google-security-research/issues/detail?id=122) 和“parallel thread corruption”。问题122是一个Adobe Flash漏洞，2014年11月被修复，这是一个奇怪的错误，在一些平台上G711声音编解码器发生了错误，编解码器在试图解码一些样品时总是导致wild copy。漏洞触发时，会导致memmove()调用使用负值作为长度参数。在现代Linux系统中，memmove()的实现似乎没有问题，而SIGSEGV处理程序干净的清理了进程。现在来看看我们所做的工作！

# 0x01 选择方法

我们要去尝试“parallel thread corruption”，也就是在线程A中观察和利用线程B的异步内存破坏.线程B的内存破坏总是会导致崩溃，我们需要并行运行两个线程。还需要赢得一个棘手的竞争条件：在线程B奔溃导致线程A退出前获取代码执行.
我们将试图让内存破坏漏洞破坏Vector.\对象的头，增大其长度字段，使其超过对象真实长度，从而获取越界读写的能力,这在Flash漏洞利用中很常见。Vector.\对象中的数据直接分配在对象头之后，在此特定漏洞里，错误的memmove()最终总是将内存中的数据向后移动2048字节。因此，如果我们分配一个足够大的Vector.\，我们就能够用对象的数据部分破坏对象的头：

![原文图片](/media/2016/06/feb85df754759fea12e3089c59b33f89)

通过偏移和对齐可以计算出，第508个uint数据元素将放置在对象头部的长度字段。

# 0x02 控制Flash堆内存

我们太忘乎所以了，需要记住线程的内存破坏发生在memmove()的wild copy中。即使我的笔记本电脑也能够以每秒大约10千兆字节的速度复制内存，所以我们将需要大量的堆内存用于复制，以使我们在复制操作访问未映射页之前，有足够的时间完成漏洞利用。得到一个大而连续的堆映射听起来很容易，但事实并非如此。如果你只是天真地在Flash堆中喷射了512KB的ByteArray缓冲区，你会看到大量的进程映射，它们看起来像这样：

```text
7fd138039000-7fd138679000 rw-p 00000000 00:00 0 7fd138679000-7fd139039000 ---p 00000000 00:00 0 7fd139039000-7fd139fb9000 rw-p 00000000 00:00 0 7fd139fb9000-7fd13a039000 ---p 00000000 00:00 0 7fd13a039000-7fd13ae79000 rw-p 00000000 00:00 0 7fd13ae79000-7fd13b039000 ---p 00000000 00:00 0 [...]
```
乍一看，作为安全专业人员，我们可能会说“太棒了！保护页！”，不过，我们这里看到的是意外的无效（accidental inefficiency） Flash堆。当试图向前扩展堆的限制时，会因为另一个映射的存在而失败，它将由操作系统决定在哪放置新的映射。默认的Linux启发式行为恰好是“在最后一次成功的映射前”，所以Flash堆将进入不能向前扩展堆的永久循环。“保护页”来自一个事实，即Flash堆（64位）将以16MB的块来保留地址空间，并根据需要以128KB为粒度来提交该区域。一系列提交的请求加起来，提交的区域也不会完全等于16MB，这很正常。这在所述的地址空间中留下了洞。这样的洞会在漏洞利用完成之前导致崩溃！
为了避免这一切，尝试获取一个可向前生长的堆，我们遵循以下步骤：

- 分配1GB大小的对象，这将被放置在当前堆区域之前。

- 在内存中喷射 4KB大小的内存（Flash堆块大小）来填冲现有的空闲块。

- 喷射更多4KB大小的内存导致堆向后延伸，最终在1GB对象之前分配一个新的16MB区域。

- 释放1GB大小的对象。
如果一切顺利，堆将是下面这个样子，绿色代表不间断增长的线性空间：
| committed: 1MB | reserved: 15MB   |       1GB hole       | committed: ~16MB | ...

# 0x03 通过堆操作对漏洞利用的对象进行布局

现在的堆状态很好，可以用来在连续的空间里构造重要的对象，就像这样：

![原文图片](/media/2016/06/fc78dbbd1818706206dca13cc6181974)

红色是我们在步骤1中决定破坏的vector，而绿色是一个对象，我们会刻意释放掉，现在堆已经建立。在开始之前，我们需要提防更多的堆特性：如堆的扩展，堆的元数据也被扩展，以便描述所有新的4KB块。堆的元数据的扩展过程将在堆内部创建空闲的块区域。这不会是一个问题，但对于另一个特性：当一个块返回给堆空闲列表时，它不是以MRU（most recently used）顺序重新分配的。因此，我们通过另一个堆喷射确保所有8KB（2块）的堆块都被分配，使特定的空闲列表为空。然后，我们释放中间的8KB缓冲区（绿色部分），使空闲堆块处于有用的地方。由于在其两侧都分配了8KB缓冲区，释放的块不会被合并，将成为下一个8KB内存分配的可选选项。
在这些堆操纵完成之后，内存中留下了一系列线性排列的整齐的重要对象，以及前面已经释放掉的8KB堆块，它将在下一次分配中被重用。

# 0x04 触发漏洞

值得一提的是，特定平台上声音子系统如何初始化也存在差异。在某些平台上，play()被第一次调用时，子系统初始化并异步启动。对于Chrome Flash插件，并不是这样。如果你看一下漏洞利用，可以看到我们通过播放不同的声音来初始化子系统，然后返回到主事件循环。这样可以确保第二次调用play()最终触发异步wild copy。
由于堆已经布局好，我们通过播放G711声音文件触发memmove()。wild copy将立即开始，并以异步方式进行，所以我们需要继续进行！G711声音解码器对象，约4424字节，但堆分配粒度在sizes >= 4096时是4096，该对象将被放置在8192大小的块中 -- 特别是如果一切正常的话，它正好落在我们刚刚释放的缓冲区里。大范围的堆损坏将在这个对象内部开始并继续向前，很快破坏掉相邻的线性排列的对象，包括矢量Vector.<uint>和 Vector.<Object>
这一刻已经触发了漏洞，我们在ActionScript循环中等待Vector.\的长度从2000改为0xfee1dead。

# 0x05 绕过ASLR

当我们可以在Vector.<uint>对象范围之外读取和写入任意4字节的值时，我们就检测到了损坏的Vector.<uint>。在步骤3中的堆布局中，与Vector.<uint>对象缓冲区相邻的对象是Vector.<Object> ，这是很理想的布局，因为它包含了我们要读取的两个重要东西：

- 指向Flash库的一个虚函数表指针的值，这让我们可以定位代码位置。

- 指向自身的指针，这很重要，因为它使我们能够把一个相对读/写转换为绝对读/写。

# 0x06 无ROP的代码执行

该漏洞利用并不通过ROP链，而是通过重写GOT / PLT中的一些函数指针。（注意，这依赖于RELRO是否存在，在常见的Linux版本和二进制文件中它很少见。如果RELRO出现了，相应的我们很有可能只针对虚函数表的指针进行重写）有了虚函数表的地址，从它开始在某个固定偏移上就能得到GOT / PLT函数指针。有相当优雅和简单的方法来让我们的代码执行：

- 读取memmove()的函数指针，这个我们已经解决了。

- 计算__libc_system的地址，它在我们刚刚读取的__memmove_ssse3_back的某个固定偏移处。

- 将__libc_system的地址写入munmap()的函数指针处。

这种设置的效果是，当程序下一次调用munmap(buffer)，它实际上将调用system(buffer)。这是有用的，因为大的ByteArray缓冲区由mmap（）/munmap（）提供，而且它被释放的时候，我们可以控制缓冲区中的确切内容。因此，我们可以向system()中传递我们想要的任意字符串。

# 0x07 继续执行与沙箱

为了说明现在我们对进程的控制，我们给system()的有效负载是“killall -STOP chrome; gnome-calculator -e 31337”。这具有两个作用：

- 显示计算器来证明我们执行了任意代码。

- 发送STOP信号给现有的Chrome进程。这将在崩溃前，有机会终止memmove()。

如果我们附加到终止的进程，观察wild copy线程，它已经成功地复制了50MB数据，即使我们试图让攻击尽可能的快和简单。
对于不希望留下任何痕迹的漏洞利用，可以很容易地连接到线程，在复制的过程中停止复制，而无需杀死线程。此外，在漏洞利用过程中发生的内存破坏可以容易地恢复。
当然，沙箱逃逸留下作为练习。该system()库调用将无法在Chrome沙盒中工作;展示利用的时候，我们使用了--no-sandbox标志，以便专注于远程代码执行。沙盒里的漏洞利用可能会通过对函数指针进行重行连接，通过异常消息来攻击IPC通道，或部署ROP链攻击内核。

# 0x08 结束语

与往常一样，Project Zero建议将所有最初的内存破坏都假定为可利用。正如我们在之前博客中展示的
[Pwn4Fun Safari exploit](http://googleprojectzero.blogspot.com/2014/07/pwn4fun-spring-2014-safari-part-i_24.html)
[glibc off-by-one NUL byte corruption](http://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html)
[lash regex exploit](http://googleprojectzero.blogspot.com/2015/02/exploitingscve-2015-0318sinsflash.html)
最初看起来无法利用，通过一两招就转化成了可利用的。
wild copy型漏洞被认为不可利用已经有了悠久的历史，但基于我们这里的研究，我们认为在复杂的多线程软件中（浏览器，浏览器插件）wild copy型漏洞可以通过parallel thread corruption的方法进行利用。我们公布这一发现，希望开发者和厂商认识到wild copy型漏洞比以前认为的更严重。

## Replies
