HackPluto's Blog

基于BROP的栈溢出漏洞利用

字数统计: 1.7k阅读时长: 6 min
2019/08/14 Share

BROP

黑客入侵软件时,有三种漏洞利用方案:
1.开源(例如,Apache)
2.开放二进制(例如,Internet Explorer)
3.封闭二进制和源(例如,某些专有网络服务)
BROP针对攻击第三种情况。
BROP攻击可以在不拥有目标二进制文件的情况下编写漏洞利用程序。它需要堆栈溢出和崩溃后重新启动的服务。根据服务是否崩溃(即连接关闭或保持打开),BROP攻击能够构建一个导致shell的完整远程攻击。BROP远程构造足够的小工具来执行写系统调用,之后二进制文件从内存传输到攻击者的套接字。之后,可以执行标准的ROP攻击。除了攻击专有服务外,BROP在定位开源软件方面非常有用,开源软件使用不公开的特定二进制文件(例如,从源设置,Gentoo框等安装)。
在针对玩具专有服务进行测试时,攻击在4,000个请求(几分钟内)内完成,可以找到nginx和MySQL中的漏洞。
有时在服务器中看到的根本问题是它们在崩溃后fork一个新的工作进程,而没有任何重新随机化(例如,fork之后没有execve),nginx就是这样的程序。

攻击原理

我来介绍下BROP如何绕过ASLR、NX、Canary 等常见的防护措施。
在 BROP 中,基本的遵循的思路如下
判断栈溢出长度
—–暴力枚举
Stack Reading
—–获取栈上的数据来泄露 canaries,以及 ebp 和返回地址。
Blind ROP
—–找到足够多的 gadgets 来控制输出函数的参数,并且对其进行调用,比如说常见的 write 函数以及 puts 函数。
Build the exploit
—–利用输出函数来 dump 出程序以便于来找到更多的 gadgets,从而可以写出最后的 exploit。

暴力枚举

可以从1开始暴力枚举,也可以最开始选一个比较大的数二分查找。

Stack Reading
函数的调用栈通常是这样的

1
buffer|canary|saved fame pointer|saved returned address

saved fame pointer 就是之前的ebp 函数开始的push ebp

我们如果需要canary或者他以后的值,需要爆破计算。但是需要注意的是,攻击条件 2 表明了程序本身并不会因为 crash 有变化,所以每次的 canary 等值都是一样的。所以我们可以按照字节进行爆破。正如论文中所展示的,每个字节最多有 256 种可能,所以在 32 位的情况下,我们最多需要爆破 1024 次,64 位最多爆破 2048 次。

Blind ROP
如果要使用write函数,最简单的方法就是构造系统调用

1
2
3
4
5
pop rdi; ret 
pop rsi; ret
pop rdx; ret
pop rax; ret # write syscall number
syscall

但通常来说,这样的方法都是比较困难的,因为想要找到一个 syscall 的地址基本不可能。我们可以通过转换为找 write 的方式来获取。

BROP gadgets
在 libc_csu_init 的结尾一长串的 gadgets,我们可以通过偏移来获取 write 函数调用的前两个参数。

find a call write
通过PLT表来寻找write函数的地址

control rdx
对于RDX寄存器,他在write函数中的作用就是输出长度的参数,只要不为0即可,通常RDX不是,不过为了更好的控制RDX,我们希望可以有类似

1
2
pop rdx;
ret;

但是事实上很少有这样的指令。但是strcmp函数会将RDX设置成要比较的字符串的长度。我们只需要找到strcmp即可。

那么我们的任务就是
1.寻找gadgets
2.通过PLT查找write和strcmp的函数地址

寻找gadgets

首先,我们来想办法寻找 gadgets。此时,由于尚未知道程序具体长什么样,所以我们只能通过简单的控制程序的返回地址为自己设置的值,从而而来猜测相应的 gadgets。而当我们控制程序的返回地址时,一般有以下几种情况
1.程序直接崩溃
2.程序运行一段时间后崩溃
3.程序一直运行而并不崩溃
为了寻找合理的 gadgets,我们可以分为以下两步

寻找 stop gadgets

所谓stop gadget一般指的是这样一段代码:当程序的执行这段代码时,程序会进入无限循环,这样使得攻击者能够一直保持连接状态。其根本的目的在于告诉攻击者,所测试的返回地址是一个 gadgets。当我们猜到某个 gadgtes 后,如果我们仅仅是将其布置在栈上,由于执行完这个 gadget 之后,程序还会跳到栈上的下一个地址。如果该地址是非法地址,那么程序就会 crash。这样的话,在攻击者看来程序只是单纯的 crash 了。因为如果是无效的gadget会引起程序的崩溃同样会crash,因此,攻击者就会认为在这个过程中并没有执行到任何的useful gadget,从而放弃它。

识别gadgets
我们将栈上定义三种区域,
1.probe
—->探针,也就是我们想要探测的代码地址。一般来说,都是 64 位程序,可以直接从 0x400000 尝试,如果不成功,有可能程序开启了 PIE 保护,再不济,就可能是程序是 32 位了。。这里我还没有特别想明白,怎么可以快速确定远程的位数。
2.Stop
—->不会使得程序崩溃的 stop gadget 的地址。
3.Trap
—->可以导致程序崩溃的地址

Advanced Rop

我们知道在 linux 中是利用_dl_runtime_resolve(link_map_obj, reloc_index) 来对动态链接的函数进行重定位的。那么如果我们可以控制相应的参数以及其对应地址的内容是不是就可以控制解析的函数了呢?答案是肯定的。具体利用方式如下
1.控制程序执行 dl_resolve 函数
2.给定 Link_map 以及 index 两个参数。
当然我们可以直接给定 plt0 对应的汇编代码,这时,我们就只需要一个 index 就足够了。
3.控制 index 的大小,以便于指向自己所控制的区域,从而伪造一个指定的重定位表项。
4.伪造重定位表项,使得重定位表项所指的符号也在自己可以控制的范围内。
5.伪造符号内容,使得符号对应的名称也在自己可以控制的范围内。

此外,这个攻击成功的很必要的条件
1.dl_resolve 函数不会检查对应的符号是否越界,它只会根据我们所给定的数据来执行。
2.dl_resolve 函数最后的解析根本上依赖于所给定的字符串。

XDCTF 2015 pwn200

CATALOG
  1. 1. BROP
  2. 2. 攻击原理
  3. 3. 寻找gadgets
  4. 4. Advanced Rop