ROP - Return Oriented Programing
基本信息
返回导向编程(ROP)是一种高级利用技术,用于规避诸如不可执行(NX)或数据执行防护(DEP)等安全措施。攻击者不是注入和执行shellcode,而是利用二进制文件或加载的库中已经存在的代码片段,称为“gadgets”。每个gadget通常以ret
指令结尾,并执行小操作,例如在寄存器之间移动数据或执行算术操作。通过链接这些gadgets,攻击者可以构建有效绕过NX/DEP保护执行任意操作的有效载荷。
ROP的工作原理
控制流劫持:首先,攻击者需要劫持程序的控制流,通常是通过利用缓冲区溢出来覆盖栈上保存的返回地址。
Gadget链接:然后,攻击者仔细选择和链接gadgets以执行所需的操作。这可能涉及为函数调用设置参数,调用函数(例如
system("/bin/sh")
),以及处理任何必要的清理或附加操作。有效载荷执行:当易受攻击的函数返回时,它不是返回到合法位置,而是开始执行gadgets链。
工具
通常可以使用ROPgadget、ropper或直接使用pwntools(ROP)来查找gadgets。
x86示例中的ROP链
x86(32位)调用约定
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')
,请注意链从以下开始:
用于对齐目的的
ret
指令(可选)system
函数的地址(假设ASLR已禁用且已知libc,更多信息请参阅Ret2lib)来自
system()
的返回地址的占位符"/bin/sh"
字符串地址(system函数的参数)
x64中的ROP链示例
x64(64位)调用约定
在类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
, 和R8
到R15
。
查找Gadgets
为了我们的目的,让我们专注于能够设置 RDI 寄存器(将 "/bin/sh" 字符串作为参数传递给 system())并调用 system() 函数的gadgets。我们假设已经识别出以下gadgets:
pop rdi; ret:将堆栈顶部的值弹出到 RDI 中,然后返回。用于为 system() 设置参数至关重要。
ret:一个简单的返回,对于某些情况下的堆栈对齐很有用。
我们知道 system() 函数的地址。
ROP链
以下是一个使用 pwntools 的示例,设置并执行一个旨在在 x64 上执行 system('/bin/sh') 的ROP链:
在这个例子中:
我们利用
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 的主要区别
由于 x64 使用寄存器传递前几个参数,因此对于简单的函数调用,通常需要的 gadget 较少,但由于寄存器数量增加和地址空间更大,找到并链接正确的 gadget 可能更复杂。x64 架构中寄存器数量的增加和地址空间的扩大为利用开发提供了机遇和挑战,特别是在返回导向编程(ROP)的背景下。
ARM64 示例中的 ROP 链
ARM64 基础知识和调用约定
查看以下页面获取这些信息:
Introduction to ARM64v8防护措施防止 ROP
堆栈 Canary:在发生缓冲区溢出时,需要绕过存储的堆栈 Canary,以覆盖返回指针以滥用 ROP 链。
Gadget 不足:如果没有足够的 gadget,将无法生成 ROP 链。
基于 ROP 的技术
请注意,ROP 只是一种执行任意代码的技术。基于 ROP,开发了许多 Ret2XXX 技术:
Ret2lib:使用 ROP 从加载的库中调用带有任意参数的任意函数(通常类似于
system('/bin/sh')
)。
Ret2Syscall:使用 ROP 准备对系统调用(例如
execve
)的调用,并使其执行任意命令。
EBP2Ret 和 EBP 链接:第一个将滥用 EBP 而不是 EIP 来控制流程,第二个类似于 Ret2lib,但在这种情况下,流程主要由 EBP 地址控制(尽管需要控制 EIP)。
其他示例和参考资料
64 位,启用 Pie 和 nx,无 Canary,用
vsyscall
地址覆盖 RIP,目的是返回到堆栈中下一个地址,这将部分覆盖地址以获取泄漏标志的函数部分arm64,无 ASLR,ROP gadget 使堆栈可执行并跳转到堆栈中的 shellcode
最后更新于