ROPping through shady corners【译文】
字数 1669 2025-08-19 12:41:44
Windows内核漏洞利用技术:Shadow ROP详解
1. 背景介绍
本文介绍了一种在Windows 10 x64系统上从用户空间向内核堆栈提供ROP链的技术,称为"Shadow ROP"。这种技术对于Ring 0级别的漏洞利用非常有用,可以有效地控制执行流程。
2. 内核漏洞类型
在Ring 0级别的漏洞利用中,主要关注两种可利用的漏洞类型:
- 任意读写漏洞:允许攻击者在内核空间(Write-What-Where)中读取/写入任意数据
- 执行流控制漏洞:允许攻击者控制内核空间中线程的执行流程(指令指针)
本文重点关注第二种类型,特别是涉及:
- 堆栈缓冲区溢出
- 释放后使用(UAF)漏洞
3. 缓解措施
Windows 10默认启用了以下三种主要的内核漏洞利用缓解措施:
-
KASLR(内核地址空间布局随机化):
- 作用不大,因为中等完整性级别的进程可以通过多种方式泄漏内核和驱动程序的基址
-
SMEP(管理员模式执行保护):
- 强大的安全防护技术
- 防止攻击者直接跳转到用户空间地址
- 绕过策略:在跳转到用户空间shellcode前先禁用SMEP
-
内核DEP(数据执行保护):
- 使内核空间中的大多数数据不可执行
4. ROP技术基础
面向返回编程(ROP)是一种流行的绕过DEP保护的技术,是ret2libc的继承者。
4.1 基本ROP链示例
以下Python代码展示了一个简单的ROP链,用于禁用SMEP并跳转到shellcode:
chain += struct.pack('<Q', kernel_base + 0x597b) # pop rcx; ret;
chain += struct.pack('<Q', 0x506f8) # rcx
chain += struct.pack('<Q', kernel_base + 0x108552) # mov cr4, rcx; ret;
chain += struct.pack('<Q', shellcode_addr)
4.2 ROP链传递问题
关键挑战:
- 如何将ROP链传递到内核空间
- 如何使堆栈指针RSP指向ROP链
在简单堆栈缓冲区溢出漏洞中,这些问题较容易解决,但在UAF等漏洞中则较为复杂。
5. 影子空间(Shadow Space)
5.1 Windows x64调用约定
根据Microsoft x64调用约定:
- 调用者负责在调用函数前在堆栈上分配32字节的"影子空间"
- 影子空间用于存放RCX、RDX、R8和R9寄存器的值
- 所有函数都必须有影子空间,即使参数少于四个的函数也是如此
关键点:
- 每个函数调用都会为被调用函数创建0x20字节的影子空间
- 如果只使用了部分影子空间,剩余部分保持未初始化状态
6. 堆栈喷射技术
j00ru在2011年发明了一种强大的堆栈喷射技术:
- 使用
nt!NtMapUserPhysicalPages进行堆栈喷射 - 可以在内核堆栈上喷射至少0x2000字节的任意数据
- 只要影子空间位于喷射范围内,就可以将任意数据植入未初始化部分
7. Shadow ROP技术原理
Shadow ROP的核心思想:
- 将ROP链的各个片段放入影子空间的未初始化部分
- 将这些片段链接起来,构建完整的ROP链
- 目标:禁用SMEP并跳转到shellcode
注意事项:
- 在喷射(
nt!NtMapUserPhysicalPages)和触发漏洞之间不要调用任何系统调用 - 使用Python等脚本语言时要特别小心,因为它们可能在底层调用系统调用
8. PoC示例分析
以HackSysTeam的HEVD v2.00中的类型混淆漏洞为例:
8.1 攻击流程
NtMapUserPhysicalPages(喷射)DeviceIoControl(触发)- ROP执行(pwn)
8.2 堆栈布局分析
使用Metasploit生成的0x2000字节模版进行喷射后,堆栈中会保留部分未破坏的子模版,这些可以作为ROP链的垫脚石。
8.3 完整ROP链示例
Windows 10 x64 V1703版本的完整ROP链:
chain += struct.pack('<Q', kernel_base + 0x68464f) # add rsp, 0x58; ret;
chain += struct.pack('<Q', 0x4141414141414141) * 11 # filler
chain += struct.pack('<Q', kernel_base + 0x11c667) # add rsp, 0x118; ret;
chain += struct.pack('<Q', 0x4141414141414141) * 35 # filler
chain += struct.pack('<Q', kernel_base + 0x597b) # pop rcx; ret;
chain += struct.pack('<Q', 0x506f8) # rcx
chain += struct.pack('<Q', kernel_base + 0x1f081e) # add rsp, 0x48; ret;
chain += struct.pack('<Q', 0x4141414141414141) * 9 # filler
chain += struct.pack('<Q', kernel_base + 0x108552) # mov cr4, rcx; ret;
chain += struct.pack('<Q', shellcode_addr)
这个ROP链使用了:
- 4个影子空间
- 3个长跳转指令
9. 关键要点总结
- Shadow ROP利用Windows x64调用约定中的影子空间未初始化部分
- 结合堆栈喷射技术将ROP链植入内核堆栈
- 通过精心构造的ROP链禁用SMEP保护
- 最终跳转到用户空间shellcode完成利用
- 在喷射和触发漏洞之间要避免任何可能破坏ROP链的系统调用