如何绕过DEP等现代防护机制实现缓冲区溢出?
TOPIC SOURCE
一文带你了解溢出漏洞
在对抗现代防护时,攻击者往往把注意力放在如何让非执行内存段重新获得执行权。DEP(Data Execution Prevention)通过在页面表项中标记 NX(No‑Execute)位,阻止 CPU 在标记为不可执行的区域跑代码;而 ASLR(Address Space Layout Randomization)则把关键模块的基址随机化,削弱硬编码地址的可靠性。两者联手,使得传统的直接覆盖返回地址后写入 shellcode 的思路几乎失效。
常见的绕过路径
- 利用不含 DEP 的旧版 DLL(如
user32.dll的某些子模块)中的jmp esp或call esp指令,实现 “代码跳转”。 - 构造 ROP 链,调用
VirtualProtect、NtProtectVirtualMemory等 API,将栈或堆页标记为可执行,然后转向自定义 shellcode。 - 返回到系统库函数(Return‑to‑libc),直接执行已有的系统调用,如
system("cmd.exe"),绕过代码注入。 - 利用堆喷射或 JIT‑spray,在 JIT 编译器生成的内存块中植入可执行指令,借助 JIT 的执行权限突破 DEP。
实战示例:ROP 关闭 DEP
假设已定位到缓冲区溢出点,偏移量为 260 字节,且目标进程使用了 kernel32.dll(ASLR 已关闭的版本)。攻击者可以在泄露的模块基址基础上,挑选以下 ROP 小碎片:
// ROP 链(伪代码)
// 1. pop eax ; ret -> eax = 0x40 (PAGE_EXECUTE_READWRITE)
// 2. pop ebx ; ret -> ebx = 0x1000 (size)
// 3. pop ecx ; ret -> ecx = 0x0 (oldProtect)
// 4. pop edx ; ret -> edx = 0x0 (lpflOldProtect)
// 5. push 0x7c801d7b ; ret // VirtualProtect 地址
// 6. int 0x80 // 调用
// 7. jmp esp // 进入后续 shellcode
在 ROP 完成后,VirtualProtect 将受控的栈页标记为可执行,随后的 jmp esp 把执行流导向后续的自定义 shellcode。此时可以使用 msfvenom -p windows/meterpreter/reverse_tcp -b "x00x0ax0d" -f c 生成不含空字符的负载,直接拼接到溢出缓冲区末端。
辅助工具与调试技巧
- Mona.py:在 Immunity Debugger 中搜索
jmp esp、call esp或者可用于 ROP 的pop reg ; ret指令。 - Ropper:快速生成符合目标平台的 ROP 链,支持自动排除已开启 ASLR 的模块。
- procmon / Process Explorer:监控目标进程的 DEP 状态,确认是否真的被关闭或可以通过
SetProcessDEPPolicy进行修改。
从理论到实践,绕过 DEP 的核心在于“让代码跑在可执行的内存”。无论是借助未受保护的 DLL、借助 ROP 调用系统 API,还是利用 JIT‑spray,思路总是围绕同一条线索展开:先取得执行权限,再把控制权交给攻击者准备的负载。真正的挑战在于在 ASLR 与 DEP 双重压制下,快速定位可靠的 gadget 并拼装出不含禁用字节的链,往往决定了攻击是否能在实战环境中落地。

参与讨论
这方法在Windows 10上还能用吗?
用ROP链挺麻烦的,有没有更简单的绕过方式?