首页
社区
课程
招聘
[原创]内核漏洞学习[6]-HEVD-UninitializedStackVariable
发表于: 2021-11-21 14:42 28509

[原创]内核漏洞学习[6]-HEVD-UninitializedStackVariable

2021-11-21 14:42
28509

# HEVD-UninitializedStackVariable

HEVD:漏洞靶场,包含各种Windows内核漏洞的驱动程序项目,在Github上就可以找到该项目,进行相关的学习

Releases · hacksysteam/HackSysExtremeVulnerableDriver · GitHub

环境准备:

Windows 7 X86 sp1 虚拟机

使用VirtualKD和windbg双机调试

HEVD 3.0+KmdManager+DubugView

TriggerUninitializedMemoryStack函数存在漏洞,内核函数栈中的变量未初始化,如果用户传入的UserValue==MagicValue(0xBAD0B0B0),就会赋值参数value和回调函数callback地址,并在后面调用callback。如果uservalue不等MagicValue,就存在利用点。

在非安全版本中UninitializedMemory变量定义但是没有初始化,由于该变量布局在栈上,它会拥有一个之前调用函数遗留的随机垃圾值。

源码:

逆向HEVD.sys
在这里插入图片描述

if (UninitializedMemory.Callback) //此处判断回调函数是否为空,否则含有空指针漏洞,可利用在0页内存上构造payload,相关HEVD:

[原创]内核漏洞学习[5]-HEVD-NullPointerDereference-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com

那么既然有判断,就要换个方法,回调函数地址修改为不为0的地址,然后该地址指向payoad,

HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK控制码对应的派遣函数UninitializedMemoryStackIoctlHandlercase

UninitializedMemoryStackIoctlHandlercase 函数调用TriggerUninitializedMemoryStack触发漏洞

逆向获得IO控制码0x22202f

在这里插入图片描述

sub_4460E8-->sub_445FFA(漏洞函数)

(1)测试

因为uservalue=0xBAD0B0B0,所以打印出信息,

在这里插入图片描述

*(PDWORD32)(buf) = 0x12345678;

将值修改为0x12345678,再次运行

我们传入值与MagicValue值不匹配时,则会触发漏洞

在这里插入图片描述

在cmp处下断点,

在这里插入图片描述

HEVD!TriggerUninitializedMemoryStack偏移 0x53处,下断点,运行

执行我们的测试exp 断下来后查看

在这里插入图片描述

在这里插入图片描述

stack_init-callback=94083ed0-940839cc=504(1284 bytes)

在这里插入图片描述

如果我们比较失败了,继续下行调用函数的时候,我们调用的将是一个内核栈上的垃圾值!此值是不固定的,它会拥有一个之前调用函数遗留的随机垃圾值。

我们已经知道了该变量距离当前栈起始位置有多远,在驱动程序源代码中看到,易受攻击的代码被try/except包围,目标操作系统不会崩溃。无法用测试exp触发系统BSOD,

现在,如果我们可以将攻击者控制的数据放在与Stack Init 0x504的偏移处,我们就可以劫持指令指针

如何从用户模式将用户控制的数据放在内核堆栈上?

j00ru](382K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6B7x3o6m8J5N6g2)9J5k6i4k6W2P5r3W2D9L8r3W2#2L8g2)9J5k6h3!0J5k6#2)9J5c8U0t1H3x3e0q4Q4x3V1j5H3y4g2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3X3c8C8k6i4u0F1k6h3I4Q4x3X3c8K6N6r3q4U0K9#2)9J5k6s2y4H3M7X3q4&6K9h3&6Y4i4K6u0V1N6r3g2U0K9r3&6A6M7i4g2W2M7#2)9J5c8W2)9J5z5g2)9J5z5g2!0q4y4g2)9^5y4W2)9&6z5g2!0q4y4#2)9&6b7g2)9^5y4q4!0q4y4W2)9&6y4W2!0n7z5g2!0q4y4W2!0n7x3#2)9&6y4g2!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4g2!0m8c8g2)9&6z5q4!0q4y4W2)9&6y4W2!0n7z5h3g2^5M7q4!0q4y4g2)9&6x3W2)9^5b7$3k6#2P5Y4A6&6M7$3g2U0N6i4u0A6N6s2W2Q4c8e0N6Q4z5e0c8Q4b7e0S2Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0W2Q4z5o6y4Q4b7V1c8Q4c8e0k6Q4z5e0S2Q4b7f1k6z5N6p5#2S2M7q4g2K6k6i4u0b7K9s2W2K6K9h3y4S2L8q4m8S2k6$3g2K6i4@1f1#2i4K6R3%4i4@1u0p5i4@1f1$3i4K6V1#2i4@1t1H3i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1#2i4@1q4q4i4K6R3K6i4@1f1%4i4K6W2m8i4K6R3@1i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1&6i4K6R3K6i4@1p5^5i4@1f1#2i4K6R3^5i4K6R3$3i4@1f1#2i4K6S2m8i4K6W2r3i4@1f1^5i4K6R3K6i4@1u0p5i4@1f1$3i4K6V1^5i4@1q4r3i4@1f1$3i4K6S2n7i4@1t1%4i4@1f1^5i4@1t1@1i4K6W2p5i4@1f1^5i4@1u0q4i4K6V1K6i4@1f1#2i4K6R3#2i4@1p5#2i4@1f1%4i4K6W2m8i4K6R3@1i4@1f1#2i4@1q4p5i4K6V1%4i4@1f1^5i4K6S2m8i4K6R3J5i4@1f1#2i4K6R3^5i4@1t1H3i4@1f1#2i4K6R3$3i4K6R3#2i4@1f1$3i4@1p5H3i4@1t1^5i4@1f1$3i4@1p5H3i4K6R3^5i4@1f1@1i4@1t1^5i4K6S2m8i4@1f1%4i4K6W2m8i4K6R3@1i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1@1i4@1t1^5i4@1q4m8i4@1f1$3i4K6W2o6i4@1q4o6i4@1f1#2i4K6W2o6i4@1t1H3i4@1f1%4i4@1u0o6i4K6V1K6i4@1f1#2i4K6R3$3i4@1t1J5i4@1f1#2i4K6S2o6i4@1u0m8i4@1f1K6i4K6R3H3i4K6R3J5i4@1f1$3i4K6W2o6i4K6R3H3i4@1f1#2i4@1p5@1i4@1p5%4i4@1f1#2i4@1t1H3i4@1u0m8i4@1f1#2i4@1q4r3i4@1t1^5i4@1f1#2i4K6S2r3i4@1q4r3i4@1f1@1i4@1u0n7i4@1p5#2i4@1f1$3i4K6S2n7i4@1t1%4i4@1f1^5i4@1t1@1i4K6W2p5x3e0l9J5y4q4)9J5b7f1W2F1N6q4m8@1M7W2)9K6b7g2)9K6b7g2y4A6P5X3g2Q4x3U0R3K6x3W2!0q4y4q4!0n7c8q4)9^5c8q4!0q4y4W2)9&6b7#2!0n7b7g2!0q4y4g2)9&6z5g2!0m8z5q4!0q4y4q4!0n7z5q4)9^5b7g2!0q4y4W2)9&6z5q4!0m8c8U0c8Q4c8e0g2Q4b7f1c8Q4z5e0N6Q4c8e0S2Q4z5p5q4Q4z5o6u0Q4c8f1k6Q4b7V1y4Q4z5o6W2Q4x3@1c8Q4x3U0k6Y4N6q4)9K6b7U0b7H3z5e0k6Q4c8e0g2Q4b7f1c8Q4z5e0N6Q4c8e0S2Q4z5p5q4Q4z5o6u0Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0g2Q4z5o6N6Q4b7V1c8Q4c8e0k6Q4z5e0g2Q4b7U0m8Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0k6Q4b7e0m8Q4z5o6S2Q4c8e0k6Q4z5f1y4Q4z5o6m8Q4c8e0g2Q4b7e0c8Q4b7e0N6Q4c8e0c8Q4b7U0W2Q4z5f1k6Q4c8e0g2Q4b7U0m8Q4b7U0p5`. 4096byte,所以传4096大小就可以占满一页内存,我们将所有内容都写成payload的地址

nt!NtMapUserPhysicalPages

鉴于 __chkstk 是一个特殊的过程,它将堆栈指针降低给定的字节数——这里是 0x10e8——该函数肯定会使用大量的本地存储(不止一个,典型的内存页!)

NtMapUserPhysicalPages调用MiCaptureUlongPtrArray

NtMapUserPhysicalPages函数分配一个包含1024的单位的本地缓冲区(就是该函数栈的缓冲区)可以在本地存储多达 4096(1024*sizeof(ULONG_PTR)) 个用户提供的字节(正好是一个内存页),

调用NtMapUserPhysicalPages之前的内核堆栈

在这里插入图片描述

调用NtMapUserPhysicalPages之后的堆栈布局

在这里插入图片描述

我们之前已经算出来,未初始化变量的callback距离stack_init 偏移0x504大小

那么我们的exp先调用NtMapUserPhysicalPages函数将我们的payload地址写入缓冲区,共写入4096字节大小,那么在我们调用漏洞函数,进而调用callback使,我们的callback值就是文章最开始说的“在非安全版本中UninitializedMemory变量定义但是没有初始化,由于该变量布局在栈上,它会拥有一个之前调用函数遗留的随机垃圾值” 所以调用callback,就会执行我们构造的payload

(2)漏洞利用

exp,可供参考

payload功能:遍历进程,得到系统进程的token,把当前进程的token替换,达到提权目的。

相关内核结构体:

在内核模式下,fs:[0]指向KPCR结构体

payload:

运行exp,喷射成功,提权成功:

在这里插入图片描述

在这里插入图片描述

注意:我们从用户模式控制内核堆栈上的数据。必须防止它被其他函数调用破坏。

为此,我们需要防止任何其他函数使用内核堆栈。只需确保在喷射内核栈并触发漏洞后不执行或调用任何其他函数即可。

内核函数栈中的变量定义后,进行初始化,比如源码给的安全版本:

nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques | j00ru//vx tech blog (vexillium.org)

Seebug

未初始化的堆栈变量 – Windows 内核开发 (payatu.com)

[原创]Windows Kernel Exploit 内核漏洞学习(6)-未初始化栈利用-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com

[翻译]Windows exploit开发系列教程第十三部分:内核利用程序之未初始化栈变量-外文翻译-看雪论坛-安全社区|安全招聘|bbs.pediy.com
ps:因为是自己边学习别记录的,就看着有点啰嗦???但是感觉写的很好理解。。

 
 
 
NTSTATUS
TriggerUninitializedMemoryStack(
    _In_ PVOID UserBuffer
)
{
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
 
#ifdef SECURE
    //
    // Secure Note: This is secure because the developer is properly initializing
    // UNINITIALIZED_MEMORY_STACK to NULL and checks for NULL pointer before calling
    // the callback
    //
 
    UNINITIALIZED_MEMORY_STACK UninitializedMemory = { 0 };//安全版本: 栈变量初始化了
#else
    //
    // Vulnerability Note: This is a vanilla Uninitialized Memory in Stack vulnerability
    // because the developer is not initializing 'UNINITIALIZED_MEMORY_STACK' structure
    // before calling the callback when 'MagicValue' does not match 'UserValue'
    //
 
    UNINITIALIZED_MEMORY_STACK UninitializedMemory;//不安全版本: 栈变量未初始化
#endif
 
    PAGED_CODE();
 
    __try
    {
        //
        // Verify if the buffer resides in user mode
        //
 
        ProbeForRead(UserBuffer, sizeof(UNINITIALIZED_MEMORY_STACK), (ULONG)__alignof(UCHAR));
 
        //
        // Get the value from user mode
        //
 
        UserValue = *(PULONG)UserBuffer;
 
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] UninitializedMemory Address: 0x%p\n", &UninitializedMemory);
 
        //
        // Validate the magic value
        //
 
        if (UserValue == MagicValue) {
            UninitializedMemory.Value = UserValue;
            UninitializedMemory.Callback = &UninitializedMemoryStackObjectCallback;
        }
 
        DbgPrint("[+] UninitializedMemory.Value: 0x%p\n", UninitializedMemory.Value);
        DbgPrint("[+] UninitializedMemory.Callback: 0x%p\n", UninitializedMemory.Callback);
 
#ifndef SECURE
        DbgPrint("[+] Triggering Uninitialized Memory in Stack\n");
#endif
 
        //
        // Call the callback function
        //
 
        if (UninitializedMemory.Callback)//在此处判断回调函数是否为0,否则可利用0页内存,
        {
            UninitializedMemory.Callback();
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }
 
    return Status;
}
NTSTATUS
TriggerUninitializedMemoryStack(
    _In_ PVOID UserBuffer
)
{
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
 
#ifdef SECURE
    //
    // Secure Note: This is secure because the developer is properly initializing
    // UNINITIALIZED_MEMORY_STACK to NULL and checks for NULL pointer before calling
    // the callback
    //
 
    UNINITIALIZED_MEMORY_STACK UninitializedMemory = { 0 };//安全版本: 栈变量初始化了
#else
    //
    // Vulnerability Note: This is a vanilla Uninitialized Memory in Stack vulnerability
    // because the developer is not initializing 'UNINITIALIZED_MEMORY_STACK' structure
    // before calling the callback when 'MagicValue' does not match 'UserValue'
    //
 
    UNINITIALIZED_MEMORY_STACK UninitializedMemory;//不安全版本: 栈变量未初始化
#endif
 
    PAGED_CODE();
 
    __try
    {
        //
        // Verify if the buffer resides in user mode
        //
 
        ProbeForRead(UserBuffer, sizeof(UNINITIALIZED_MEMORY_STACK), (ULONG)__alignof(UCHAR));
 
        //
        // Get the value from user mode
        //
 
        UserValue = *(PULONG)UserBuffer;
 
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] UninitializedMemory Address: 0x%p\n", &UninitializedMemory);
 
        //
        // Validate the magic value
        //
 
        if (UserValue == MagicValue) {
            UninitializedMemory.Value = UserValue;
            UninitializedMemory.Callback = &UninitializedMemoryStackObjectCallback;
        }
 
        DbgPrint("[+] UninitializedMemory.Value: 0x%p\n", UninitializedMemory.Value);
        DbgPrint("[+] UninitializedMemory.Callback: 0x%p\n", UninitializedMemory.Callback);
 
#ifndef SECURE
        DbgPrint("[+] Triggering Uninitialized Memory in Stack\n");
#endif
 
        //
        // Call the callback function
        //
 
        if (UninitializedMemory.Callback)//在此处判断回调函数是否为0,否则可利用0页内存,
        {
            UninitializedMemory.Callback();
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }
 
    return Status;
}
 
 
 
case HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK:
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
Status = UninitializedMemoryStackIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
break;
case HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK:
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
Status = UninitializedMemoryStackIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
break;
 
 
 
 
#include<stdio.h>
#include<Windows.h>
HANDLE hDevice = NULL;
 
#define HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_NEITHER, FILE_ANY_ACCESS)
//#define HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK                    IOCTL(0x80B)
int main()
 
{
 
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL
        );
    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        printf("[-]failed to get device handle !");
        return FALSE;
 
    }
    printf("[+]success to get device  handle");
 
    if (hDevice) {
 
        DWORD bReturn = 0;
        char buf[4] = { 0 };
        *(PDWORD32)(buf) = 0xBAD0B0B0;
 
        DeviceIoControl(hDevice, 0x22202f, buf, 4, NULL, 0, &bReturn, NULL);
    }
 
 
 
}
#include<stdio.h>
#include<Windows.h>
HANDLE hDevice = NULL;
 
#define HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_NEITHER, FILE_ANY_ACCESS)
//#define HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK                    IOCTL(0x80B)
int main()
 
{
 
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL
        );
    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        printf("[-]failed to get device handle !");
        return FALSE;
 
    }
    printf("[+]success to get device  handle");
 
    if (hDevice) {
 
        DWORD bReturn = 0;
        char buf[4] = { 0 };
        *(PDWORD32)(buf) = 0xBAD0B0B0;
 
        DeviceIoControl(hDevice, 0x22202f, buf, 4, NULL, 0, &bReturn, NULL);
    }
 
 
 
}
 
 
 
 
 
 
 
 
kd> bp HEVD!TriggerUninitializedMemoryStack+0x53
kd> g
kd> bp HEVD!TriggerUninitializedMemoryStack+0x53
kd> g
 

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

最后于 2021-11-21 16:14 被pyikaaaa编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (1)
雪    币: 731
活跃值: (222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享。
2022-1-17 20:25
0
游客
登录 | 注册 方可回帖
返回