[10] HEVD 内核漏洞之TypeConfusing

0x00 前言

本篇是HEVD系列的最后一片学习文章,这段时间,通过阅读前人文章,自己分析源码,调试文件,小有收获。

感谢前人的付出与分享,让新手能很快的上手,握爪。

实验环境:Win10专业版+VMware Workstation 15 Pro+Win7 x86 sp1

实验工具:VS2015+Windbg+KmdManager+DbgViewer

0x01 漏洞原理

类型混淆漏洞,顾名思义,通过分析代码逻辑,使得原先不可利用的类型变量或者缓冲区,修改其类型后,变得可以利用。

先来看看漏洞函数:

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
NTSTATUS TriggerTypeConfusion(
_In_ PUSER_TYPE_CONFUSION_OBJECT UserTypeConfusionObject
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PKERNEL_TYPE_CONFUSION_OBJECT KernelTypeConfusionObject = NULL;

PAGED_CODE();

__try
{
//
// Verify if the buffer resides in user mode
//

ProbeForRead(
UserTypeConfusionObject,
sizeof(USER_TYPE_CONFUSION_OBJECT),
(ULONG)__alignof(UCHAR)
);

//
// Allocate Pool chunk
//

KernelTypeConfusionObject = (PKERNEL_TYPE_CONFUSION_OBJECT)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(KERNEL_TYPE_CONFUSION_OBJECT),
(ULONG)POOL_TAG
);

if (!KernelTypeConfusionObject)
{
//
// Unable to allocate Pool chunk
//

DbgPrint("[-] Unable to allocate Pool chunk\n");

Status = STATUS_NO_MEMORY;
return Status;
}
else
{
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));
DbgPrint("[+] Pool Size: 0x%X\n", sizeof(KERNEL_TYPE_CONFUSION_OBJECT));
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelTypeConfusionObject);
}

DbgPrint("[+] UserTypeConfusionObject: 0x%p\n", UserTypeConfusionObject);
DbgPrint("[+] KernelTypeConfusionObject: 0x%p\n", KernelTypeConfusionObject);
DbgPrint("[+] KernelTypeConfusionObject Size: 0x%X\n", sizeof(KERNEL_TYPE_CONFUSION_OBJECT));

KernelTypeConfusionObject->ObjectID = UserTypeConfusionObject->ObjectID;
KernelTypeConfusionObject->ObjectType = UserTypeConfusionObject->ObjectType;

DbgPrint("[+] KernelTypeConfusionObject->ObjectID: 0x%p\n", KernelTypeConfusionObject->ObjectID);
DbgPrint("[+] KernelTypeConfusionObject->ObjectType: 0x%p\n", KernelTypeConfusionObject->ObjectType);


#ifdef SECURE
//
// Secure Note: This is secure because the developer is properly setting 'Callback'
// member of the 'KERNEL_TYPE_CONFUSION_OBJECT' structure before passing the pointer
// of 'KernelTypeConfusionObject' to 'TypeConfusionObjectInitializer()' function as
// parameter
//

KernelTypeConfusionObject->Callback = &TypeConfusionObjectCallback;
Status = TypeConfusionObjectInitializer(KernelTypeConfusionObject);
#else
DbgPrint("[+] Triggering Type Confusion\n");

//
// Vulnerability Note: This is a vanilla Type Confusion vulnerability due to improper
// use of the 'UNION' construct. The developer has not set the 'Callback' member of
// the 'KERNEL_TYPE_CONFUSION_OBJECT' structure before passing the pointer of
// 'KernelTypeConfusionObject' to 'TypeConfusionObjectInitializer()' function as
// parameter
//

Status = TypeConfusionObjectInitializer(KernelTypeConfusionObject);
#endif

DbgPrint("[+] Freeing KernelTypeConfusionObject Object\n");
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelTypeConfusionObject);

//
// Free the allocated Pool chunk
//

ExFreePoolWithTag((PVOID)KernelTypeConfusionObject, (ULONG)POOL_TAG);
KernelTypeConfusionObject = NULL;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}

return Status;
}

明显的,可以看到在漏洞函数中,不安全的版本中不恰当地使用了内核结构体联合体,未重新初始化联合体中的callback成员,导致直接默认使用ObjectType的值,当,ObjectType指向恶意代码,则被利用。

1
2
3
4
5
6
7
8
9
typedef struct _KERNEL_TYPE_CONFUSION_OBJECT
{
ULONG_PTR ObjectID;
union
{
ULONG_PTR ObjectType;
FunctionPointer Callback;
};
} KERNEL_TYPE_CONFUSION_OBJECT, *PKERNEL_TYPE_CONFUSION_OBJECT;

0x02 漏洞利用

核心思路:

Ring3 用户结构体成员objectType赋值为payload地址。

1
2
3
4
typedef struct _USER_TYPE_CONFUSION_OBJECT {
ULONG_PTR objectID; //对象ID
ULONG_PTR objectType;//对象类型 赋值为payload地址
} USER_TYPE_CONFUSION_OBJECT, *PUSER_TYPE_CONFUSION_OBJECT;

DeviceIoControl进入驱动

1
2
3
4
5
6
7
8
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_TYPE_CONFUSION,
(LPVOID)UserTypeConfusionObject,
sizeof(USER_TYPE_CONFUSION_OBJECT),
NULL,
0,
&BytesReturned,
NULL);

上面构造的结构体传入驱动程序,驱动程序用申请的_KERNEL_TYPE_CONFUSION_OBJECT结构内存进行接收。

而在处理callback成员时,不进行初始化,当内核中调用callback函数时,则内核执行利用代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NTSTATUS
TypeConfusionObjectInitializer(
_In_ PKERNEL_TYPE_CONFUSION_OBJECT KernelTypeConfusionObject
)
{
NTSTATUS Status = STATUS_SUCCESS;

PAGED_CODE();

DbgPrint("[+] KernelTypeConfusionObject->Callback: 0x%p\n", KernelTypeConfusionObject->Callback);
DbgPrint("[+] Calling Callback\n");

KernelTypeConfusionObject->Callback();

DbgPrint("[+] Kernel Type Confusion Object Initialized\n");

return Status;
}

具体利用代码可参考这里,利用结果如下:

img

0x03 漏洞反思

安全版本中,提到了及时初始化回调函数成员的方法。

1
2
KernelTypeConfusionObject->Callback = &TypeConfusionObjectCallback;
Status = TypeConfusionObjectInitializer(KernelTypeConfusionObject);

题外话:最近学习中,刚好看到一个类型混淆的利用,在18-8174中,Vbs脚本里通过精心构造内存,将0x08 BSRT混淆,0x200C,实现内存任意读写,感兴趣的童鞋可以研究下。

0x04 后记

基础部分先到这里啦,后面有新的成果,还会继续分享。继续前进,冲冲冲

img