01_Rop-Ret2text介绍以及插件
ROP攻击技术
ROP(Return-Oriented Programming,返回导向编程)是一种攻击技术,攻击者利用受害程序中已存在的代码片段(称为“gadget”)来执行任意代码。ROP 技术常用于绕过 DEP(Data Execution Prevention,数据执行保护)等安全机制。
ROP 攻击的基本原理
代码重用:攻击者不需要在堆栈中插入自己的代码(如传统的 shellcode),而是利用已有的、合法程序中的代码片段(gadget)执行恶意操作。这些 gadget 通常是程序中一些小的指令序列,通常以“ret”(返回指令)结束,返回会控制程序的执行流。
跳转到 gadget:攻击者通过堆栈溢出等方式控制程序的返回地址,劫持程序的控制流,让程序按攻击者的意愿依次跳转到预期的 gadget 处。
执行控制:攻击者通过选择合适的 gadget 实现与恶意代码相同的效果,如操作寄存器、内存,调用系统调用等。
ROP 的步骤
寻找 gadget:攻击者需要寻找程序或共享库中合适的 gadget。这些 gadget 通常是一些较短的指令序列,可能执行简单的操作,如将某个值加载到寄存器、对寄存器内容进行操作,或者跳转到另一个 gadget。
例如:
pop eax; ret
xor eax, eax; ret
控制返回地址:利用栈溢出、格式化字符串漏洞等手段,将特定的 gadget 地址放入栈中,替代原本的返回地址。
构造 ROP 链:通过在栈中连续放入 gadget 的地址,使程序按攻击者设计的顺序依次执行这些 gadget,形成一条 ROP 链。通过这些 gadget,攻击者可以达到类似 shellcode 的效果。
绕过 DEP
DEP 是一种防止代码在不可执行区域(如栈或堆)运行的机制。ROP 通过复用程序中已有的代码来执行恶意操作,从而绕过了 DEP。
典型用法
执行系统调用:攻击者可以通过 ROP 构造一系列系统调用(如
execve()
),从而执行恶意代码。提升权限:通过 ROP 技术调用特定系统函数提升进程权限。
防御 ROP 的技术
ASLR(Address Space Layout Randomization):通过随机化程序的内存布局,使得攻击者无法轻易找到 gadget 的准确地址。
栈金丝雀:在函数栈帧中插入特殊的值(金丝雀),如果在函数返回之前该值发生改变,则检测到溢出攻击。
Control Flow Integrity(控制流完整性):确保程序的执行流不会被任意劫持,攻击者无法随意跳转到 gadget。
ROP 是一种极具威胁的攻击方式,它利用了程序中现有的代码而非插入恶意代码,因此传统的执行保护机制对其无效。
cyclic
使用步骤
1. 生成一个循环模式
可以使用pwntools
库生成一个不重复的字节模式,用于触发栈溢出并找出精确的覆盖位置。首先,确保已经安装pwntools
库:
pip install pwntools
然后,可以使用cyclic
函数生成一个特定长度的循环模式。假设你想生成300字节的模式:
from pwn import *
pattern = cyclic(300)
print(pattern)
这会生成一段如"aaaabaaacaaadaaaeaaafaaa..."
的字符串,并且每个字符组的长度和顺序都是不重复的,便于后续调试。
2. 触发栈溢出并崩溃程序
接下来,将生成的模式传递给目标程序。你可以通过手动输入、管道传递或者通过修改代码直接传入这个模式,具体方式取决于程序的输入方式。
假设你的目标程序通过命令行参数接收输入:
./vulnerable_program $(cyclic 300)
这样运行程序,栈溢出时程序会崩溃,并且你可以通过gdb
获取程序崩溃时的寄存器信息。
3. 通过gdb调试获取崩溃地址
使用gdb
调试崩溃的程序。运行命令:
gdb ./vulnerable_program
在gdb中使用如下命令:
run $(cyclic 300)
程序崩溃时,gdb
会显示类似这样的输出:
Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
这里的0x61616161
表示栈上的地址已经被'aaaa'
覆盖了。
4. 找到偏移量
为了确定返回地址被覆盖的精确偏移,可以使用pwntools
的cyclic_find
函数:
from pwn import *
offset = cyclic_find(0x61616161)
print(offset)
这个命令会输出偏移量,假设结果是112
,说明在输入的第112
字节处覆盖了栈上的返回地址。
5. 替换返回地址并进一步调试
知道偏移量后,你可以尝试通过修改输入,使其在覆盖返回地址的位置插入一个新的有效地址。例如,覆盖栈返回地址并劫持程序流:
payload = fit({
112: p32(0xdeadbeef)
})
然后再用gdb
调试这个修改过的输入,继续分析程序行为。
gdb-peda
gdb-peda
是一个增强 gdb
调试体验的插件,它集成了许多常用的调试功能,尤其是在漏洞利用和安全研究中非常有用。在 gdb-peda
中,pattern
命令可以用来生成和查找特定模式,类似于 pwntools
中的 cyclic
功能。pattern create
是其中一个常用的子命令,用于生成独特的字节模式,通常用于检测栈溢出和找到返回地址的偏移。
gdb-peda
中 pattern create
的使用
1. 安装 gdb-peda
如果还没有安装 gdb-peda
,可以通过以下命令安装:
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
重新启动 gdb
后,peda
就会被自动加载。
2. pattern create
命令
pattern create
用来生成一个指定长度的不重复的字节模式,用于后续的栈溢出调试和分析。生成的模式可以被传递到目标程序,之后可以通过程序崩溃的地址来确定溢出发生的位置。
生成模式
使用 pattern create
来生成指定长度的模式:
gdb-peda$ pattern create 300
这将生成一个300字节长度的模式,类似于:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3...
这些字节组不重复并按照特定的顺序排列。你可以把这个模式作为输入传递给目标程序,帮助你在栈溢出时确定覆盖了哪一部分栈内存。
3. 使用模式来定位崩溃点
当目标程序因为栈溢出崩溃时,你可以在 gdb-peda
中查看崩溃发生时的寄存器信息,比如 EIP
或 RIP
的值。如果 EIP
被模式覆盖了,你会看到一些不常见的值。
假设程序崩溃时 EIP
显示为 0x41336241
,这表明 EIP
被模式的一部分覆盖了。
4. pattern offset
命令
使用 pattern offset
来查找栈溢出覆盖的位置。假设我们看到 EIP
被 0x41336241
覆盖了,我们可以通过以下命令找到它在模式中的偏移量:
gdb-peda$ pattern offset 0x41336241
gdb-peda
会返回一个类似这样的输出:
109
这表示在模式生成的字节流中的第 109
个字节处覆盖了 EIP
,所以可以确定我们需要在输入的第109个字节开始构造覆盖 EIP
的数据。
5. 替换返回地址
知道了偏移量之后,你可以通过构造输入来覆盖 EIP
并劫持控制流。例如,你可以在偏移位置插入你想要的返回地址(比如 0xdeadbeef
):
payload = "A" * 109 + "\xef\xbe\xad\xde"
这个 payload
将在偏移109字节的位置插入地址 0xdeadbeef
,用于劫持程序流。
6. 验证漏洞利用
将构造的 payload
传递给目标程序,并通过 gdb-peda
继续调试,确认是否成功劫持了程序流。