BROP - Blind Return Oriented Programming

支持 HackTricks

基本信息

此攻击的目标是能够利用缓冲区溢出滥用 ROP,而无需有关易受攻击的二进制文件的任何信息。 此攻击基于以下情景:

  • 一个堆栈漏洞和如何触发它的知识。

  • 一个在崩溃后重新启动的服务器应用程序。

攻击

1. 找到易受攻击的偏移量发送一个额外字符,直到检测到服务器的故障

2. 暴力破解 canary 以泄漏它

3. 暴力破解存储的 RBP 和 RIP 地址以泄漏它们

您可以在这里(BF 分叉和线程化堆栈 Canary)这里(BF 地址在堆栈中)找到有关这些过程的更多信息。

4. 找到停止小工具

这个小工具基本上允许确认通过 ROP 小工具执行了一些有趣的内容,因为执行没有崩溃。通常,这个小工具将是停止执行的东西,并且当寻找用于确认特定 ROP 小工具是否被执行的 ROP 小工具时,它位于 ROP 链的末尾。

5. 找到 BROP 小工具

此技术使用 ret2csu 小工具。这是因为如果在一些指令中访问此小工具,您将获得控制 rsirdi 的小工具:

这些将是小工具:

  • pop rsi; pop r15; ret

  • pop rdi; ret

请注意,使用这些小工具可以控制函数的 2 个参数

还要注意,ret2csu 小工具具有非常独特的签名,因为它将从堆栈中弹出 6 个寄存器。因此,发送如下链:

'A' * 偏移量 + canary + rbp + 地址 + 0xdead * 6 + STOP

如果执行了 STOP,这基本上意味着使用了一个从堆栈中弹出 6 个寄存器的地址。或者使用的地址也是一个 STOP 地址。

为了消除这种可能性,执行一个新链如下,并且必须不执行 STOP 小工具以确认之前的链确实弹出了 6 个寄存器:

'A' * 偏移量 + canary + rbp + 地址

知道 ret2csu 小工具的地址后,就可以推断出控制 rsirdi 的小工具的地址

6. 找到 PLT

PLT 表可以从 0x400000 或从堆栈中的泄漏 RIP 地址(如果使用了 PIE)开始搜索。表的条目以 16B(0x10B)分隔,当调用一个函数时,即使参数不正确,服务器也不会崩溃。此外,检查 PLT 中的一个条目的地址 + 6B 也不会崩溃,因为这是第一个执行的代码。

因此,可以通过检查以下行为来找到 PLT 表:

  • 'A' * 偏移量 + canary + rbp + 地址 + STOP -> 无崩溃

  • 'A' * 偏移量 + canary + rbp + (地址 + 0x6) + STOP -> 无崩溃

  • 'A' * 偏移量 + canary + rbp + (地址 + 0x10) + STOP -> 无崩溃

7. 查找 strcmp

strcmp 函数将寄存器 rdx 设置为正在比较的字符串的长度。请注意,rdx 是第三个参数,我们需要它大于 0,以便稍后使用 write 泄漏程序。

可以根据其行为在 PLT 中找到 strcmp 的位置,利用我们现在可以控制函数的前两个参数的事实:

  • strcmp(<非读取地址>, <非读取地址>) -> 崩溃

  • strcmp(<非读取地址>, <读取地址>) -> 崩溃

  • strcmp(<读取地址>, <非读取地址>) -> 崩溃

  • strcmp(<读取地址>, <读取地址>) -> 无崩溃

可以通过调用 PLT 表的每个条目或使用PLT 慢路径来检查这一点,该慢路径基本上包括调用 PLT 表中的一个条目 + 0xb(调用到 dlresolve),然后在堆栈中跟随希望探测的条目号(从零开始)以扫描所有 PLT 条目:

  • strcmp(<非读取地址>, <读取地址>) -> 崩溃

  • b'A' * 偏移量 + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> 将崩溃

  • strcmp(<读取地址>, <非读取地址>) -> 崩溃

  • b'A' * 偏移量 + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<读取地址>, <读取地址>) -> 无崩溃

  • b'A' * 偏移量 + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

请记住:

  • BROP + 0x7 指向 pop RSI; pop R15; ret;

  • BROP + 0x9 指向 pop RDI; ret;

  • PLT + 0xb 指向调用 dl_resolve

找到 strcmp 后,就可以将 rdx 设置为大于 0 的值。

请注意,通常 rdx 将已经包含一个大于 0 的值,因此此步骤可能不是必要的。

### 8. 寻找Write函数或等效函数

最后,需要一个用于外泄数据以外泄二进制文件的gadget。此时可以控制2个参数并设置rdx大于0

有3个常见的函数可以被滥用:

  • puts(data)

  • dprintf(fd, data)

  • write(fd, data, len(data)

然而,原始论文只提到了**write**函数,所以让我们来谈谈它:

当前问题是我们不知道write函数在PLT中的位置,也不知道要将数据发送到我们的套接字的fd号

但是,我们知道PLT表的位置,可以根据其行为找到write。我们可以与服务器创建多个连接,并使用高FD,希望它与我们的某些连接匹配。

查找这些函数的行为特征:

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 如果有数据打印出来,则找到了puts

  • 'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 如果有数据打印出来,则找到了dprintf

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 如果有数据打印出来,则找到了write

自动化利用

参考资料

Last updated