首页
社区
课程
招聘
[翻译]Windows内核漏洞利用教程5:空指针引用
发表于: 2018-1-22 11:25 4890

[翻译]Windows内核漏洞利用教程5:空指针引用

2018-1-22 11:25
4890


原文地址:https://rootkits.xyz/blog/2018/01/kernel-null-pointer-dereference/

该系列已翻译文章
在这个系列中,在上一篇详细的文章之后,本文将分析一个理解起来相对简单的漏洞类型。空指针引用漏洞,当一个指针的值为空时,却被调用指向某一块内存地址时,就产生了空指针引用漏洞。显然,问题很清楚,如果我们可以控制NULL page,并且在那里写入东西,我们就可能得到代码执行权限。你可能已经猜到,我们将使用和上一篇文章一样的方法去申请NULL page,布局我们的shellcode。所以这一片文章中的很多信息将依赖上一篇文章。
再次感谢hacksysteam提供的驱动程序。
看一下 NullPointerDereference.c   文件。
NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
    PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
 
    PAGED_CODE();
 
    __try {
        // 检查buffer是否属于用户态
        ProbeForRead(UserBuffer,
                     sizeof(NULL_POINTER_DEREFERENCE),
                     (ULONG)__alignof(NULL_POINTER_DEREFERENCE));
 
        // 申请pool 块
        NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
                                  ExAllocatePoolWithTag(NonPagedPool,
                                                        sizeof(NULL_POINTER_DEREFERENCE),
                                                        (ULONG)POOL_TAG);
 
        if (!NullPointerDereference) {
            // 申请失败
            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(NULL_POINTER_DEREFERENCE));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
        }
 
        // 获取用户态传进来的值
        UserValue = *(PULONG)UserBuffer;
 
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
 
        //检查magic值
        if (UserValue == MagicValue) {
            NullPointerDereference->Value = UserValue;
            NullPointerDereference->Callback = &NullPointerDereferenceObjectCallback;
 
            DbgPrint("[+] NullPointerDereference->Value: 0x%p\n", NullPointerDereference->Value);
            DbgPrint("[+] NullPointerDereference->Callback: 0x%p\n", NullPointerDereference->Callback);
        }
        else {
            DbgPrint("[+] Freeing NullPointerDereference Object\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
 
            // 释放申请的pool块
            ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
 
            // 设置指针为空
            NullPointerDereference = NULL;
        }
 
#ifdef SECURE
        // 安全代码:在调用回调函数之前,检查指针是否为空
        if (NullPointerDereference) {
            NullPointerDereference->Callback();
        }
#else
        DbgPrint("[+] Triggering Null Pointer Dereference\n");
 
        // 不安全代码:在调用回调函数之前,没有检查指针是否为空
        NullPointerDereference->Callback();
#endif

NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
    PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
 
    PAGED_CODE();
 
    __try {
        // 检查buffer是否属于用户态
        ProbeForRead(UserBuffer,
                     sizeof(NULL_POINTER_DEREFERENCE),
                     (ULONG)__alignof(NULL_POINTER_DEREFERENCE));
 
        // 申请pool 块
        NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
                                  ExAllocatePoolWithTag(NonPagedPool,
                                                        sizeof(NULL_POINTER_DEREFERENCE),
                                                        (ULONG)POOL_TAG);
 
        if (!NullPointerDereference) {
            // 申请失败
            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(NULL_POINTER_DEREFERENCE));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
        }
 
        // 获取用户态传进来的值
        UserValue = *(PULONG)UserBuffer;
 
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
 
        //检查magic值
        if (UserValue == MagicValue) {
            NullPointerDereference->Value = UserValue;
            NullPointerDereference->Callback = &NullPointerDereferenceObjectCallback;
 
            DbgPrint("[+] NullPointerDereference->Value: 0x%p\n", NullPointerDereference->Value);
            DbgPrint("[+] NullPointerDereference->Callback: 0x%p\n", NullPointerDereference->Callback);
        }
        else {
            DbgPrint("[+] Freeing NullPointerDereference Object\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
 
            // 释放申请的pool块
            ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
 
            // 设置指针为空
            NullPointerDereference = NULL;
        }
 
#ifdef SECURE
        // 安全代码:在调用回调函数之前,检查指针是否为空
        if (NullPointerDereference) {
            NullPointerDereference->Callback();
        }
#else
        DbgPrint("[+] Triggering Null Pointer Dereference\n");
 
        // 不安全代码:在调用回调函数之前,没有检查指针是否为空
        NullPointerDereference->Callback();
#endif

在代码中,用magic值(0xBAD0B0B0)和用户态的值相比较,如果不相同,NullPointerDereference指针被设置为空。我们可以看到,在安全的代码中,检查了NullPointerDereference指针是否为空。


从IDA简单分析可以看出,非分页pool的Tag 为“Hack”,magic值,以及偏移0x4,我们要写入shellcode的地方。
这次要使用的IOCTL值为0x22202b.


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 3302
活跃值: (1144)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
2
作者又写了第7篇  教程,未初始化的堆变量
2018-3-24 22:06
0
游客
登录 | 注册 方可回帖
返回
//