BROP - Blind Return Oriented Programming
基本信息
此攻击的目标是能够利用ROP通过缓冲区溢出而无需有关易受攻击的二进制文件的任何信息。 此攻击基于以下情景:
存在堆栈漏洞并且知道如何触发它。
一个在崩溃后重新启动的服务器应用程序。
攻击
1. 查找易受攻击的偏移量,发送一个额外字符直到检测到服务器发生故障
2. 暴力破解canary 以泄漏它
3. 暴力破解存储的RBP和RIP地址以泄漏它们
您可以在这里(BF分叉和线程化堆栈Canaries)和这里(BF地址在堆栈中)找到有关这些过程的更多信息。
4. 查找停止gadget
这个gadget基本上允许确认通过ROP gadget执行了一些有趣的操作,因为执行没有崩溃。通常,这个gadget将是一些停止执行的内容,并且当寻找ROP gadget以确认特定ROP gadget已执行时,它将位于ROP链的末尾。
5. 查找BROP gadget
此技术使用ret2csu gadget。这是因为如果在一些指令中访问此gadget,则可以获得控制**rsi
和rdi
**的gadgets:
这些将是gadgets:
pop rsi; pop r15; ret
pop rdi; ret
请注意,使用这些gadgets可以控制函数的2个参数。
还要注意,ret2csu gadget具有非常独特的签名,因为它将从堆栈中弹出6个寄存器。因此,发送如下链:
'A' * 偏移量 + canary + rbp + ADDR + 0xdead * 6 + STOP
如果执行STOP,这基本上意味着使用了一个从堆栈中弹出6个寄存器的地址。或者使用的地址也是一个STOP地址。
为了排除这种可能性,执行一个新链如下,它不应执行STOP gadget以确认先前的链确实弹出了6个寄存器:
'A' * 偏移量 + canary + rbp + ADDR
知道ret2csu gadget的地址后,就可以推断出控制rsi
和rdi
的gadgets的地址。
6. 查找PLT
PLT表可以从0x400000或从堆栈中的泄漏的RIP地址(如果使用PIE)开始搜索。表的条目以16B(0x10B)分隔,当调用一个函数时,即使参数不正确,服务器也不会崩溃。此外,检查PLT + 6B中的条目地址也不会崩溃,因为这是第一个执行的代码。
因此,可以通过检查以下行为来找到PLT表:
'A' * 偏移量 + canary + rbp + ADDR + STOP
-> 无崩溃'A' * 偏移量 + canary + rbp + (ADDR + 0x6) + STOP
-> 无崩溃'A' * 偏移量 + canary + rbp + (ADDR + 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
自动化利用
参考资料
最后更新于