最新消息:图 床

從 CVE-2016-0165 說起:分析、利用和檢測(中)

COOL IAM 164浏览 0评论

作者:Leeqwind
作者博客:https://xiaodaozhi.com/exploit/42.html

本文將對 CVE-2016-0165 (MS16-039) 漏洞進行一次簡單的分析,並嘗試構造其漏洞利用和內核提權驗證代碼,以及實現對應利用樣本的檢測邏輯。分析環境為 Windows 7 x86 SP1 基礎環境的虛擬機,配置 1.5GB 的內存。

本文分為三篇:

從 CVE-2016-0165 說起:分析、利用和檢測(上)

從 CVE-2016-0165 說起:分析、利用和檢測(中)

從 CVE-2016-0165 說起:分析、利用和檢測(下)

0x5 利用

前面驗證了漏洞的觸發機理,接下來將通過該漏洞實現任意地址讀寫的利用目的。


AddEdgeToGET

根據前面的章節,實現觸發該漏洞並引發後續的 OOB 導致系統 BSOD 發生,但由於函數代碼邏輯中 cCurves 等相關域的值和實際分配的用來容納 EDGE 表項的緩衝區大小的差異實在太大,在 AddEdgeToGET 函數中會進行極大範圍內的內存訪問(超過 4GB 地址空間範圍)。不利於在漏洞觸發后使系統平穩過渡,好在 AddEdgeToGET 函數中存在忽略當前邊而直接返回的判斷邏輯:

  if ( pClipRect )
  {
    if ( iYEnd < pClipRect->top || iYStart > pClipRect->bottom )
      return pFreeEdge;
    if ( iYStart < pClipRect->top )
    {
      bClip = 1;
      iYStart = pClipRect->top;
    }
    if ( iYEnd > pClipRect->bottom )
      iYEnd = pClipRect->bottom;
  }
  ipFreeEdge_Y = (iYStart + 15) >> 4;
  *((_DWORD *)pFreeEdge + 3) = ipFreeEdge_Y;
  *((_DWORD *)pFreeEdge + 1) = ((iYEnd + 15) >> 4) - ipFreeEdge_Y;
  if ( ((iYEnd + 15) >> 4) - ipFreeEdge_Y <= 0 )
    return pFreeEdge;

清單 5-1 函數 AddEdgeToGET 中忽略邊的判斷邏輯

函數中存在兩處跳過當前邊而直接返回的判斷邏輯,返回時由於忽略當前邊的數據,所以 pFreeEdge 指針不向後移。第一處返回邏輯可以不做關注,因為 pClipRectAddEdgeToGET 函數的最後 1 個參數,該參數同樣作為最後 1 個參數從函數 RGNMEMOBJ::vCreatevConstructGET 直接傳遞,而在 NtGdiPathToRegion 函數中調用 RGNMEMOBJ::vCreate 時該參數傳值為 0,如清單 2-1 中所示,所以不可能命中條件。第二處返回邏輯的判斷條件是:當前兩點描述的邊中,結束坐標點的 Y 軸坐標是否與起始坐標點的 Y 軸坐標相等;如果 Y 軸坐標相等,則忽略這條邊,直接返回當前 pFreeEdge 指針指向的地址。此處的右移 4 比特位只是在還原之前在 EPATHOBJ::createrecEPATHOBJ::growlastrec 函數中存儲坐標點時左移 4 比特位的數值。

因此可以利用函數的這個特性,通過修改用戶進程中傳入的各坐標點數據,可以控制緩衝區中 EDGE 元素使用的個數。但由於緩衝區中的 EDGE 元素是逐個寫入的,因此通過控制各坐標點的 Y 軸坐標值只能控制從起始位置開始連續寫入的 EDGE 個數,而不能控制跳過某些元素節點。

接下來就是最關鍵且最複雜的部分:內核內存布局。


內核內存布局

內核池風水技術是用來控制內核內存布局的關鍵技術。通過在分配關鍵的內核對象之前,首先分配和釋放特定長度和數量的其他對象,使內核內存首先處於一個確定的狀態,來確保在分配關鍵的內核對象時,能夠被系統內存管模塊分配到我們所希望其分配到的某些位置,例如接近某些可控對象的位置。後續利用漏洞通過巧妙使用某些“讀寫原語”對所分配的關鍵內核對象後面的內存區域進行操作,以控制原本不能控制的相鄰對象的成員數據,這些數據將作為後續利用操作的重要節點。

在着手實施內核內存布局之前,有必要首先了解一下 Windows 的內存池分配機制。在 Windows 系統中,調用 ExAllocatePoolWithTag 分配不超過 0x1000 字節長度的池內存塊時,會使用到 POOL_HEADER 結構,作為分配的池內存塊的頭部。在當前系統環境下 POOL_HEADER 結構的定義:

kd> dt _POOL_HEADER
nt!_POOL_HEADER
   +0x000 PreviousSize     : Pos 0, 9 Bits
   +0x000 PoolIndex        : Pos 9, 7 Bits
   +0x002 BlockSize        : Pos 0, 9 Bits
   +0x002 PoolType         : Pos 9, 7 Bits
   +0x000 Ulong1           : Uint4B
   +0x004 PoolTag          : Uint4B
   +0x004 AllocatorBackTraceIndex : Uint2B
   +0x006 PoolTagHash      : Uint2B

清單 5-2 結構 POOL_HEADER 的定義

在 32 位 Windows 系統環境下 POOL_HEADER 體現在返回值指針向前 8 字節的位置:

win32k!RGNMEMOBJ::vCreate+0xc5:
933a3ffc ff1550005293    call    dword ptr [win32k!_imp__ExAllocatePoolWithTag (93520050)]
kd> dc esp l4
93d1b400  00000021 00000b68 6e677247 00000005  !...h...Grgn....
kd> p
win32k!RGNMEMOBJ::vCreate+0xcb:
933a4002 8b5510          mov     edx,dword ptr [ebp+10h]
kd> r eax
eax=fd674180
kd> !pool fd674180
Pool page fd674180 region is Paged session pool
fd674000 is not a valid large pool allocation, checking large session pool...
 fd674158 size:    8 previous size:    0  (Allocated)  Frag
 fd674160 size:   18 previous size:    8  (Free)       Free
*fd674178 size:  b70 previous size:   18  (Allocated) *Grgn
         Pooltag Grgn : GDITAG_REGION, Binary : win32k.sys
 fd674ce8 size:  318 previous size:  b70  (Allocated)  Gfnt
kd> dc fd674178 l4
fd674178  476e0003 6e677247 00000000 00000000  ..nGGrgn........

清單 5-3 分配內存池時 POOL_HEADER 結構的位置

在調用 ExFreePoolWithTag 函數釋放先前分配的池內存塊時,系統會校驗目標內存塊和其所在內存頁中相鄰的塊的 POOL_HEADER 結構;如果檢測到塊的 POOL_HEADER 被破壞,將會拋出導致系統 BSOD 的 BAD_POOL_HEADER 異常。但在一種情況下例外:那就是如果該池內存塊位於所在的內存頁的末尾,那麼在這次 ExFreePoolWithTag 函數調用期間將不會對相鄰內存塊進行這個校驗。

根據前面的章節可知,漏洞所在函數 RGNMEMOBJ::vCreate 中分配了用於存儲中間 EDGE 數據的內存池塊,並在函數結束時釋放了分配的內存塊,所以在這種情況下,就肯定會面臨釋放內存塊時校驗相鄰 POOL_HEADER 的問題。而如果在 RGNMEMOBJ::vCreate 函數中分配內存塊時,能使其分配的內存塊處於所在內存頁的末尾,後續的 OOB 將會發生在下一個內存頁中,雖然會破壞下一內存頁中的內存塊,但至少在當前函數調用期間釋放內存塊時不去校驗相鄰塊的 POOL_HEADER 結構,問題就得以解決。

转载请注明:IAMCOOL » 從 CVE-2016-0165 說起:分析、利用和檢測(中)

0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x