Remote Desktop UAF漏洞藍屏,SQL Server 報偏移量爲???的位置執行 讀取 期間,操作系統已經向 SQL Server 返回了錯誤 21

這是一臺雲服務器,非物理主機,藍屏轉儲 memory.dmp, 不及格的程序員-八神, 每次SqlServer數據庫不可用時,在相近時間點上能看到系統藍屏的系統日誌,猜測與藍屏重啓有關。

C:\ProgramData\Microsoft\Windows\WER\ReportArchive\

https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2019-0708

3: kd> !analyze -v termdd
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: 0000000000000000, memory referenced
Arg2: 0000000000000002, IRQL
Arg3: 0000000000000000, value 0 = read operation, 1 = write operation
Arg4: fffff88002e67006, address which referenced memory
STACK_TEXT:  
fffff880`088c9a88 fffff800`016bcf69     : 00000000`0000000a 00000000`00000000 00000000`00000002 00000000`00000000 : nt!KeBugCheckEx
fffff880`088c9a90 fffff800`016bad88     : 00000000`00000000 00000000`00000000 fffffa80`06cdc900 fffffa80`08abfd60 : nt!KiBugCheckDispatch+0x69
fffff880`088c9bd0 fffff880`02e67006     : fffffa80`08abfd60 fffffa80`08abfd60 fffffa80`08ada010 fffff880`088c9df0 : nt!KiPageFault+0x448
fffff880`088c9d60 fffff880`02e66e01     : fffffa80`08a74940 fffffa80`08046100 fffff8a0`003ab000 fffff880`088c9fa0 : termdd!IcaChannelInputInternal+0x1f2
fffff880`088c9e40 fffff880`0466f198     : fffff8a0`035de010 fffff880`02e6bd6c fffffa80`076f1740 fffffa80`0938b730 : termdd!IcaChannelInput+0xdd
fffff880`088c9e80 fffff880`0464c708     : 00000000`00000000 fffff800`00000001 00000000`00000000 00000000`00000000 : RDPWD!SignalBrokenConnection+0x54
fffff880`088c9ee0 fffff880`02e66d8f     : fffffa80`08a74940 fffffa80`076f1a20 fffffa80`076f1740 00000000`c000013c : RDPWD!WDLIB_MCSIcaChannelInput+0x90
fffff880`088c9f30 fffff880`046306a4     : fffffa80`076f1a20 fffffa80`076f1a20 fffffa80`076f1740 fffffa80`076f1a20 : termdd!IcaChannelInput+0x6b
fffff880`088c9f70 fffff880`02e6af3e     : fffffa80`07430720 fffffa80`0933bf20 fffffa80`08c8f010 fffffa80`08368750 : tdtcp!TdInputThread+0x64c
fffff880`088ca7f0 fffff880`02e69ae3     : fffffa80`08552cb0 fffffa80`08368750 fffffa80`07189d80 fffffa80`08c8f010 : termdd!IcaDriverThread+0x5a
fffff880`088ca820 fffff880`02e689e9     : fffffa80`088dd420 fffff880`088ca958 fffff880`088ca960 00000000`00000000 : termdd!IcaDeviceControlStack+0x827
fffff880`088ca900 fffff880`02e68689     : 00000000`00000000 fffffa80`08368750 00000000`00000000 fffffa80`088dd538 : termdd!IcaDeviceControl+0x75
fffff880`088ca950 fffff800`0190dd9a     : 00000000`00000002 00000000`00000002 00000000`00000000 fffffa80`08c80660 : termdd!IcaDispatch+0x215
fffff880`088ca990 fffff800`01ad3831     : fffffa80`08c80660 fffffa80`08c80660 fffffa80`08c80660 fffff880`01efa180 : nt!IopSynchronousServiceTail+0xfa
fffff880`088caa00 fffff800`019655d6     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0xc51
fffff880`088cab40 fffff800`016bcbd3     : 00000000`80000001 fffff800`01ab3b46 00000000`00000000 00000000`00000000 : nt!NtDeviceIoControlFile+0x56
fffff880`088cabb0 00000000`77a898fa     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
00000000`01eff818 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77a898fa

SYMBOL_NAME:  termdd!IcaChannelInputInternal+1f2

MODULE_NAME: termdd

IMAGE_NAME:  termdd.sys

STACK_COMMAND:  .cxr; .ecxr ; kb

FAILURE_BUCKET_ID:  0xD1_termdd!IcaChannelInputInternal+1f2

OS_VERSION:  7.1.7601.24384

BUILDLAB_STR:  win7sp1_ldr_escrow

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 7

 系統日誌與應用日誌報告:不及格的程序員-八神

 

 

 

 

 

 

 

 

 

 


 之前也出現的,3-15日

 

 

 

 

 

 

 

 


 

參考:

POC已公開!RDP遠程代碼執行漏洞被利用引發藍屏-ip功擊藍屏

作者:上猶日期:2020-04-07 12:58:36

返回目錄:電腦藍屏

2019年5月31日,深信服安全團隊發現公開的CVE-2019-0708的POC已在github上流傳,並第一時間進行復現以及分析。經測試,該POC可導致目標主機藍屏崩潰,特此再次發出預警。

漏洞名稱:Remote Desktop Protocol漏洞

威脅等級:嚴重

影響範圍:Windows XP、Windows 7 、Windows Server 2003、Windows Server 2008、Windows Server 2008 R2

漏洞類型:任意代碼執行漏洞

利用難度:容易

目前漏洞攻擊方式已經公開,後續極有可能發生利用漏洞的攻擊事件。深信服提醒廣大用戶,務必及時更新安全補丁!

漏洞復現

根據該POC的發佈者稱,該POC針對Windows Server 2008 R2 x64。深信服安全團隊第一時間進行了復現,證實該POC確實可導致目標主機的藍屏崩潰並重啓。

測試結果表明,除此版本系統外,該POC還會影響Windows 7 sp1 x64,Windows 7 sp1 x86,Windows Server 2008 x86。(其餘尚未進行驗證)

攻擊者只需以受害主機IP地址爲參數執行一個python文件,即可成功實施攻擊,最終目標系統處於藍屏崩潰狀態。

漏洞分析

成功利用漏洞的前提是將名稱爲“MS_T120”的靜態信道成功綁定到正常信道以外。由於微軟官方在內部會使用一個名爲MS_T120的信道,因此此信道理論上不應該接收任意消息。

然而,RDP的內部組件有很多,包括svchost.exe中的幾個用戶模式的dll,以及幾個內核模式的驅動程序。攻擊者在MS_T120信道上發送消息,這將導致termdd.sys驅動程序中觸發一個雙重釋放漏洞(free-after-free)。具體分析如下:

客戶端在完成RDP協議的握手過程後,將開始向已經綁定的各個信道中發送消息。MS_T120信道的消息是由用戶模式組件——rdpwsx.dll進行管理,該dll創建一個線程,該線程則將在函數rdpwsx!IoThreadFunc中進行循環,通過I/O端口讀取消息。

數據通過I/O數據包傳入後,將進入rdpwsx!MCSPortData 函數進行處理:

從函數中可以看到,在rdpwsx!MCSPortData 函數中,有兩個opcode:0x0和0x2。如果opcode爲0x2,則調用rdpwsx!HandleDisconnectProviderIndication函數執行清理動作,然後使用rdpwsx!MCSChannelClose關閉通道。

從理論上來說,發送的數據包大小都在合法範圍的MS_T120消息,將會正常關閉MS_T120信道,並斷開連接程序(此時opcode爲0x2),並且進行清理操作,此時將不會產生遠程代碼執行和藍屏崩潰的風險。但如果發送的數據包大小無效,就會導致遠程代碼執行和拒絕服務。

影響範圍

目前受影響的Windows版本:

Microsoft Windows XP

Microsoft Windows Server 2008 R2 for x64-based Systems SP1

Microsoft Windows Server 2008 R2 for Itanium-based Systems SP1

Microsoft Windows Server 2008 for x64-based Systems SP2

Microsoft Windows Server 2008 for Itanium-based Systems SP2

Microsoft Windows Server 2008 for 32-bit Systems SP2

Microsoft Windows Server 2003

Microsoft Windows 7 for x64-based Systems SP1

Microsoft Windows 7 for 32-bit Systems SP1

解決方案

1.漏洞檢測

深信服雲眼在漏洞爆發之初,已完成檢測更新,對所有用戶網站探測,保障用戶安全。不清楚自身業務是否存在漏洞的用戶,可註冊信服雲眼賬號,獲取30天免費體驗。註冊地址爲:

https://saas.sangfor.com.cn

2.修復建議

(1)及時安裝微軟發佈的安全更新補丁:

Microsoft官方已經在 2019年5月14日修復了該漏洞,用戶可以通過安裝微軟的安全更新來給系統打上安全補丁,下載地址爲:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708

同時針對已不受微軟更新支持的系統Windows Server 2003和Windows XP提供的安全更新,下載地址:

https://support.microsoft.com/zh-cn/help/4500705/customer-guidance-for-cve-2019-0708

(2)緩解措施(在無法及時安裝微軟安全更新的情況下作爲臨時性解決方案):

若用戶不需要用到遠程桌面服務,建議禁用該服務。

開啓網絡級別身份驗證(NLA),此方案適用於Windows 7、 Windows Server 2008、Windows Server 2008 R2。

以上緩解措施只能暫時性針對該漏洞對系統進行部分緩解,強烈建議在條件允許的情況下及時安裝微軟安全更新。

參考鏈接

[1].https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708

[2].https://securingtomorrow.mcafee.com/other-blogs/mcafee-labs/rdp-stands-for-really-do-patch-understanding-the-wormable-rdp-vulnerability-cve-2019-0708/

[3].https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/5073f4ed-1e93-45e1-b039-6e30c385867c

[4].https://zerosum0x0.blogspot.com/2019/05/avoiding-dos-how-bluekeep-scanners-work.html


 

CVE-2019-0708(BlueKeep): 利用RDP PDU將數據寫入內核的3種方式

閱讀量433997

|評論3

|

發佈時間 : 2019-09-02 11:00:23

x
譯文聲明

本文是翻譯文章,文章原作者 paloaltonetworks,文章來源:unit42.paloaltonetworks.com

原文地址:https://unit42.paloaltonetworks.com/exploitation-of-windows-cve-2019-0708-bluekeep-three-ways-to-write-data-into-the-kernel-with-rdp-pdu/

譯文僅供參考,具體內容表達以及含義原文爲準。

 

0x00 前言

2019年5月,微軟針對遠程代碼執行(RCE)漏洞CVE-2019-0708專門發佈了帶外補丁更新包,這個漏洞也就是知名的“BlueKeep”漏洞,存在於遠程桌面服務(RDS)種。這是一個預身份認證漏洞,無需用戶交互,因此很有可能被攻擊者利用,帶來破壞性風險。如果成功利用漏洞,攻擊者就能以SYSTEM權限執行任意代碼。根據微軟安全響應中心(MSRC)發佈的公告,這是一種蠕蟲級漏洞,可能被攻擊者用來發起類似Wannacry及EsteemAudit級別的攻擊。瞭解到這個漏洞的嚴重性及可能對公衆造成的潛在風險後,微軟也採取了罕見的處理流程,爲不再受支持的Windows XP系統推出補丁,以全面保護Windows用戶。

由於該漏洞可能會帶來全球範圍內的災難性後果,Palo Alto Networks Unit 42研究人員認爲該漏洞非常值得研究,以便澄清RDS的內部工作原理及漏洞利用方式。我們深入研究了RDP內部實現以及如何利用這些內部工作流程在未打補丁的主機上實現代碼執行。本文討論瞭如何利用Bitmap Cache PDU(協議數據單元)、Refresh Rect PDU以及RDPDR Client Name Request PDU將數據寫入內核內存中。

自微軟在5月份發佈補丁以來,該漏洞得到了計算機安全行業的廣泛關注。事實上漏洞利用工具的公開並在實際攻擊中使用只是一個時間問題,根據我們的研究成果,大家可以瞭解到存在漏洞的系統實際上會面臨極大的風險。

 

0x01 Bitmap Cache PDU

根據MS-RDPBCGR(Remote Desktop Protocol: Basic Connectivity and Graphics Remoting)文檔描述,Bitmap Cache PDU的全稱爲TS_BITMAPCACHE_PERSISTENT_LIST_PDU,這是一種Persistent Key List PDU Data,內嵌在Persistent Key List PDU中。Persistent Key List PDU是一種RDP Connection Sequence(連接時序)PDU,在RDP Connection Sequence的Connection Finalization(連接完成)階段由客戶端發送至服務端,如圖1所示。

圖1. RDP連接時序

Persistent Key List PDU頭部爲通用的RDP PDU頭,具體格式如圖2所示:tpktHeader(4字節)+x224Data(3字節)+mcsSDrq(可變字節)+securityHeader(可變字節)。

圖2. Client Persistent Key List PDU

根據MS-RDPBCGR文檔,TS_BITMAPCACHE_PERSISTENT_LIST_PDU結構中包含由之前會話中發送的一系列已緩存的bitmap key(位圖鍵值),這些key與Cache Bitmap(Revision 2)Order(緩存位圖序)相對應,如圖3所示:

圖3. Persistent Key List PDU Data(BITMAPCACHE PERSISTENT LIST PDU)

在官方設計方案中,RDP客戶端可以使用Bitmap Cache PDU向服務端發送信息,表明客戶端本地包含與這些key對應的位圖拷貝,這意味着服務端不需要重新將位圖發送給客戶端。根據MS-RDPBCGR文檔描述,Bitmap PDU有如下4個特點:

  • RDP服務端會分配一個內核池,用來存儲已緩存的bitmap key;
  • RDP服務端分配的內核池大小由RDP客戶端發送過來的BITMAPCACHE PERSISTENT LIST結構中的numEntriesCacheXX取0到4之間的值)以及totalEntriesCacheXX取0到4之間的值)字段所控制,這兩個字段大小均爲2字節(WORD);
  • Bitmap Cache PDU可以被多次發送,因爲bitmap key可以通過多個Persistent Key List PDU發送,每個PDU通過bBitMask字段中flag的來標記;
  • bitmap key的數量最多爲169個。

根據BITMAPCACHE PERSISTENT LIST PDU的這4個特點,如果我們能繞過bitmap key數量限制(169個),或者微軟在實現RDP時沒有遵循這個限制值,那麼就有可能將任意數據寫入內核中。

 

0x02 如何通過Bitmap Cache PDU將數據寫入內核

根據MS-RDPBCGR文檔,正常加密的BITMAPCACHE PERSISTENT LIST PDU如下所示:

f2 00 -> TS_SHARECONTROLHEADER::totalLength = 0x00f2 = 242 bytes
17 00 -> TS_SHARECONTROLHEADER::pduType = 0x0017

0x0017

= 0x0010 | 0x0007

= TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU

ef 03 -> TS_SHARECONTROLHEADER::pduSource = 0x03ef = 1007

ea 03 01 00 -> TS_SHAREDATAHEADER::shareID = 0x000103ea

00 -> TS_SHAREDATAHEADER::pad1

01 -> TS_SHAREDATAHEADER::streamId = STREAM_LOW (1)

00 00 -> TS_SHAREDATAHEADER::uncompressedLength = 0

2b -> TS_SHAREDATAHEADER::pduType2 =

PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST (43)

00 -> TS_SHAREDATAHEADER::generalCompressedType = 0

00 00 -> TS_SHAREDATAHEADER::generalCompressedLength = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[0] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[1] = 0

19 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[2] = 0x19 = 25

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[3] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[4] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[0] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[1] = 0

19 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[2] = 0x19 = 25

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[3] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[4] = 0

03 -> TS_BITMAPCACHE_PERSISTENT_LIST::bBitMask = 0x03

0x03

= 0x01 | 0x02

= PERSIST_FIRST_PDU | PERSIST_LAST_PDU

00 -> TS_BITMAPCACHE_PERSISTENT_LIST::Pad2

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::Pad3

TS_BITMAPCACHE_PERSISTENT_LIST::entries:

a3 1e 51 16 -> Cache 2, Key 0, Low 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key1)

48 29 22 78 -> Cache 2, Key 0, High 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key2)

61 f7 89 9c -> Cache 2, Key 1, Low 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key1)

cd a9 66 a8 -> Cache 2, Key 1, High 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key2)

…

RDPWD.sys內核模塊中,ShareClass::SBC_HandlePersistentCacheList函數負責解析BITMAPCACHE PERSISTENT LIST PDU。當結構體中的bBitMask字段值設置爲0x01時,表示當前PDU爲PERSIST FIRST PDU。隨後SBC_HandlePersistentCacheList會調用WDLIBRT_MemAlloc來分配一個內核池(分配內核空間)來存儲持久性位圖緩存鍵值,如圖4所示。其他情況下,0x00值表示當前PDU爲PERSIST MIDDLE PDU,0x02值表示當前PDU爲PERSIST LAST PDU。當解析PERSIST MIDDLE PDU以及PERSIST LAST PDU時,SBC_HandlePersistentCacheList會將bitmap cache key緩存到前面分配的內存空間中,如圖5所示。

圖4. SBC_HandlePersistentCacheList分配池空間以及檢查totalEntriesCacheLimi

圖5. SBC_HandlePersistentCacheList拷貝bitmap cache key

Windows 7 x86系統中調用棧情況如圖6所示,以參數形式傳遞給SBC_HandlePersistentCacheList函數的TS_BITMAPCACHE_PERSISTENT_LIST結構體如圖7所示。

圖6. SBC_HandlePersistentCacheList棧軌跡

圖7. 以SBC_HandlePersistentCacheList第二個參數形式傳入的TS_BITMAPCACHE_PERSISTENT_LIST結構

如圖4所示,bitmapCacheListPoolLen = 0xC * (total length + 4),而total length = totalEntriesCache0 + totalEntriesCache1 + totalEntriesCache2 + totalEntriesCache3 + totalEntriesCache4。根據這個公式,我們可以設置totalEntriesCacheX=0xffff,這樣bitmapCacheListPoolLen就能取得最大值。然而如圖8所示,系統會對每個totalEntriesCacheX檢查totalEntriesCacheLimittotalEntriesCacheLimitX來自於TS_BITMAPCACHE_CAPABILITYSET_REV2結構體,當RDPWD調用DCS_Init時,CAPAPI_LOAD_TS_BITMAPCACHE_CAPABILITYSET_REV2函數就會初始化這個結構體。當解析Confirm Active PDU時,CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2函數就會將這些值組合起來,如圖9所示。

圖8. RDPWD!CAPAPI_LOAD_TS_BITMAPCACHE_CAPABILITYSET_REV2

圖9. RDPWD!CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2

CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2會將服務端初始化的NumCellCaches0x03)及totalEntriesCacheLimit[0-4]0x2580x2580x100000x00x0,如圖9中edx寄存器所示)與客戶端請求中的NumCellCaches0x03)及totalEntriesCache[0-4]0x800002580x800002580x8000fffc0x00x0,如圖9中esi寄存器所示)組合起來。客戶端可以控制NumCellCachestotalEntriesCache[0-4],如圖10所示,但不能超過服務端初始化的NumCellCaches0x03)及totalEntriesCacheLimit[0-4]0x2580x2580x100000x00x0),如圖11隨時。

圖10. TS_BITMAPCACHE_CAPABILITYSET_REV2

圖11. CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2函數

瞭解這些知識後,我們可以計算出bitmapCacheListPoolLen的最大值,最大值bitmapCacheListPoolLen = 0xC * (0x10000 + 0x258 + 0x258 + 4) = 0xc3870,理論上我們可以在內核池中控制大小爲0x8 * (0x10000 + 0x258 + 0x258 + 4) = 0x825a0字節數據,如圖12所示。

圖12. 轉儲出的Persistent Key List PDU內存

然而,我們發現RDP客戶端並沒有按我們設想的方式來控制位圖緩存列表池中的所有數據。每8字節可控數據之間都有一個4字節不可控數據(索引值),這對shellcode利用來說非常不友好。此外,0xc3870字節大小的內核池不能被多次分配,因爲正常情況下Persistent Key List PDU只能發送1次。然而經過多次測試後,我們發現內核池會在同一個內存地址處進行分配。此外,在分配的位圖緩存列表池之前始終會有系統分配的大小爲0x2b522c(x86系統)或0x2b5240(x64系統)字節的內核池,這一點對堆佈局而言非常重要(尤其是在x64系統中),如圖13所示。

圖13. 統計Persistent Key List PDU的佈局特徵

 

0x03 Refresh Rect PDU

根據MS-RDPBCGR文檔,RDP客戶端可以通過Refresh Rect PDU請求服務端重繪會話屏幕中的1個或多個矩形區域。這個結構體中包含通用PDU頭以及refreshRectPduData(可變)字段,如圖14所示。

圖14. Refresh Rect PDU Data

numberOfAreas字段是一個8比特無符號整數,用來定義areasToRefresh字段中Inclusive Rectangle結構的數量。areaToRefresh是包含TS_RECTANGLE16結構的一個數組,如圖15所示。

圖15. Inclusive Rectangle(TS_RECTANGLE16

Refresh Rect PDU用來通知服務端一系列“Inclusive Rectangle”屏幕區域,以便服務端重繪會話屏幕區域中的一個或多個矩形區域。這個PDU基於默認打開的一個信道進行傳輸,信道ID爲0x03ea(Server Channel ID)。當連接時序完成後,如圖1所示,RDP服務端就可以接收/解析Refresh Rect PDU,並且這裏最重要的一點在於Refresh Rect PDU可以多次發送。雖然TS_RECTANGLE16大小隻有8字節,這意味着RDP客戶端只能控制8字節數據,但這依然可以作爲將數據寫入內核的一個切入點。

 

0x04 如何通過Refresh Rect PDU將數據寫入內核

正常解密後的Refresh Rect PDU如圖16所示。

圖16. 解密後的Refresh Rect PDU

RDPWD.sys內核模塊中的WDW_InvalidateRect函數負責解析Refresh Rect PDU,如圖17所示:

圖17. RDPWD!WDW_InvalidateRect棧軌跡

如圖18所示,WDW_InvalidateRect函數會解析Refresh Rect PDU數據流,從數據流中提取numberOfAreas字段值作爲循環計數值。由於numberOfAreas屬於1字節字段,最大值爲0xFF,因此最大循環計數值也爲0xFF。在循環中,WDW_InvalidateRect函數會分別提取TS_RECTANGLE16結構中的lefttoprightbottom字段值,將其放入棧上的一個結構中,然後作爲WDICART_IcaChannelInput函數的第5個參數傳入。這裏需要提一句,WDICART_IcaChannelInput的第6個參數爲常數值0x808,這個值對堆噴射來說非常有用。

圖18. RDPWD!WDW_InvalidateRect函數

WDICART_IcaChannelInput最終會調用termdd.sys內核模塊中的IcaChannelInputInternal函數。如圖19所示,如果滿足一系列判斷條件,那麼IcaChannelInputInternal函數就會調用ExAllocatePoolWithTag來分配大小爲inputSize_6th_para + 0x20的內核池。因此,當RDPWD!WDW_InvalidateRect調用IcaChannelInputInternal函數時,inputSize_6th_para=0x808,並且內核池的大小爲0x828

圖19. termdd!IcaChannelInputInternal中的ExAllocatePoolWithTagmemcpy操作

如何內核池分配成功完成,系統就會調用memcpy,將input_buffer_2拷貝到新分配的內核池內存中。當調用方爲RDPWD!WDW_InvalidateRect時,memcpy所使用的參數如圖20所示:

圖20. 在windbg中觀察termdd!IcaChannelInputInternal對應的memcpy操作

有趣的是,memcpy函數的源地址來自於RDPWD!WDW_InvalidateRect棧上的stRect結構,並且只有前3個DWORD會在RDPWD!WDW_InvalidateRect中設置,如圖21所示。棧上的其他內存處於未初始化狀態,因此容易導致信息泄露。此外,使用大小爲0x808的內存空間來存儲12字節的數據也是非常適用於堆噴射場景。

圖21. RDPWD!WDW_InvalidateRect stRect結構集

根據這些信息,當RDP客戶端向服務端發送一個Refresh Rect PDU,且numberOfAreas字段值爲0xFF時,RDP服務端就會調用termdd!IcaChannelInputInternal 0xFF次。每次termdd!IcaChannelInputInternal調用都會分配0x828大小的內核池內存空間,將客戶端可控的8字節TS_RECTANGLE16拷貝到該內核池中。因此,numberOfAreas字段值爲0xFF的1個Refresh Rect PDU就可以分配0xFF個大小爲0x828字節的內核池。從理論上講,如果RDP客戶端發送0x200次Refresh Rect PDU,那麼RDP服務端就會分配大約0x20000個大小爲0x828的非分頁內核池。考慮到0x828大小的內核池會按照0x1000進行對齊,因此會同時佔據非常大範圍的內核池,客戶端可控的8字節數據會被複制到每個0x1000內核池中0x02c固定偏移處。如圖22所示,我們可以通過Refresh Rect PDU在內核中構建非常穩定的池噴射。

圖22. RDPWD!WDW_InvalidateRect噴射

在某些情況下,當termdd!_IcaQueueReadChannelRequest修改某個指針時(圖23中的v14變量),判斷條件爲False,此時ExAllocatePoolWithTag以及memcpy並不會被調用,代碼也不會進入_IcaCopyDataToUserBuffer分支,這樣將導致池無法成功分配。然而,當多次發送Refresh Rect PDU時,儘管某些池無法成功分配,我們還是可以成功形成內核池噴射。

此外在某些情況下,當RDP服務端使用完有些內核池後,可能會釋放掉這些內核池,但內核池的數據並不會被清除,這樣我們噴射到內核中的數據依然可以在漏洞利用過程中使用。

圖23. termdd!IcaChannelInputInternal中的IcaCopyDataToUserBuffer

 

0x05 RDPDR Client Name Request PDU

根據MS-RDPEFS文檔描述,RDPDR Client Name Request PDU在“Remote Desktop Protocol: File System Virtual Channel Extension”(文件系統虛擬信道擴展)中指定,該擴展運行在名爲RDPDR的靜態虛擬信道(static virtual channel)中。MS-RDPEFS協議的目的是將訪問流從服務端重定向到客戶端文件系統。Client Name Request是客戶端發往服務端的第二個PDU,如圖24所示。

圖24. File System Virtual Channel Extension協議初始化

客戶端使用Client Name Request PDU將主機名發送給服務端,如圖25所示。

圖25. Client Name Request(DR_CORE_CLIENT_NAME_REQ

這裏頭部爲4字節的RDPDR_HEADERComponent字段值爲RDPDR_CTYP_COREPacketId字段值爲PAKID_CORE_CLIENT_NAMEComputerNameLen(4字節)是一個32位無符號整數,用來指定ComputerName字段的字節數。ComputerName字段(可變)是一個長度可變的ASCII或Unicode字符數組,具體格式由UnicodeFlag字段所決定,這個字符串用來標識客戶端的計算機名。

 

0x06 如何通過RDPDR Client Name Request PDU將數據寫入內核

典型的RDPDR Client Name Request PDU數據如下圖所示。正常情況Client Name Request PDU可以被多次發送,對於每個請求,RDP服務端會分配一個內核池來存儲這些信息,最重要的是:RDP客戶端可以完全控制PDU的內容和長度。這也是將數據寫入內核內存的一個絕佳切入點。典型的RDPDR Client Name Request PDU如圖26所示。

圖26. Client Name Request內存佈局

當RDP服務端收到一個RDPDR Client Name Request PDU時,就會調用termdd.sys內核模塊中的IcaChannelInputInternal函數來調度(dispatch)信道數據,然後調用RDPDR模塊來解析Client Name Request PDU的數據部分。這裏的IcaChannelInputInternal函數在處理Client Name Request PDU的代碼邏輯上與對Refresh Rect PDU的處理邏輯相同。該函數會調用ExAllocatePoolWithTag(使用TSic標籤)來分配內核內存,然後使用memcpy將Client Name Request數據拷貝到新分配的內核內存中,如圖27所示。

圖27. Client Name Request

到目前爲止,我們已經知道服務端拷貝的數據內容及長度可以被RDP客戶端可以控制,並且Client Name Request PDU也可以多次發送。正是因爲這種靈活性及友好性,我們可以使用Client Name Request PDU來構造針對已釋放內核池的UAF(釋放後重用)漏洞利用場景,也可以用來將shellcode寫入內核池,甚至可以用來將客戶端可控的數據連續噴射到內核內存中。

如圖28所示,我們成功實現了穩定的池分配,通過RDPDR Client Name Request PDU將客戶端可控的數據寫入內核池中。

圖28. 利用Client Name Request PDU實現穩定池分配

0x07 檢測及緩解

CVE-2019-0708是針對RDP的一個嚴重漏洞,可以被未經身份認證的攻擊者所使用。根據MSRC安全公告,Windows XP、Windows 2003、Windows 7以及Windows 2008都受該漏洞影響。大家應該儘快給自己的Windows系統打上補丁,以緩解該威脅。如果條件允許,用戶應當禁用或者限制通過外部接口訪問RDP資源。

 

0x08 總結

在本文中,我們介紹了通過RDP PDU將數據寫入內核的3種方法:

  • Bitmap Cache PDU可以讓RDP服務端在分配0x2b5200大小的內核池後,分配0xc3870大小的內核池,並寫入客戶端可控的數據,然而無法多次執行分配0xc3870大小的內核池操作。
  • Refresh Rect PDU可以用來噴射0x828大小的多個內核池,這些內核池以0x1000方式對齊,並將客戶端可控的8字節寫入0x828大小的每個內核池中。
  • RDPDR Client Name Request PDU可以用來噴射大小可控的內核池,並填充客戶端可控的數據。

我們認爲還有其他未公開的方式,可以讓CVE-2019-0708的利用方式更加簡單及穩定。用戶應當採取措施,通過前面提到的緩解方法保護可能存在風險的系統。


 

在2019年5月的補丁週期中,Microsoft在其遠程桌面服務(RDS)中發佈了一個遠程代碼執行錯誤補丁。遠程未經身份驗證的攻擊者可以通過將精心設計的RDP消息發送到目標服務器來利用此漏洞。成功利用可能會導致執行具有管理權限的任意代碼。雖然我們對此漏洞的初步研究主要集中在緩解和保護上,但趨勢科技安全研究團隊的Pengsu Cheng,Kamlapati Choubey和Saran Neti致力於徹底分析漏洞。以下是趨勢科技漏洞研究服務報告的摘錄,內容涵蓋CVE-2019-0708,並進行了一些最小的修改。

漏洞

Microsoft遠程桌面服務(以前稱爲終端服務)允許用戶遠程打開交互式Windows會話。遠程桌面服務提供與基於終端的環境類似的功能,其中多個終端(客戶端)可以連接到單個主機(服務器)。遠程用戶可以登錄到遠程主機並訪問主機上的數據,運行應用程序等。遠程桌面連接默認使用遠程桌面協議(RDP)通過端口3389 / TCP與遠程服務器通信。

RDP指定多個會議參與者如何查看和協作共享程序。該協議是ITU-T T.128應用程序共享協議的Microsoft擴展。該協議利用T.120標準中較低層協議提供的其他服務,例如T.124通用會議控制(GCC),T.122多點通信服務(MCS)等。

RDP連接以連接序列消息開始,由遠程桌面協議:基本連接和圖形遠程處理(MS-RDPBCGR)協議定義,如下所示:

每條消息的格式可以在[ 1 ]中找到。該漏洞與“MCS Connect Initial and GCC Create”請求有關。

收到“X.224連接確認”響應後,從客戶端發送到服務器的“MCS Connect Initial and GCC Create”請求。“MCS Connect Initial and GCC Create”請求包含與安全相關的信息,虛擬通道創建信息以及其他受支持的RDP客戶端功能。“MCS Connect Initial and GCC Create”請求的結構如下:

除tpktHeader字段外,所有多字節整數都是小端字節順序。

•X.224層通常可以具有多種PDU類型並由任意長度組成,但“MCS Connect Initial and GCC Create”數據包具有3字節x224結構。
•mcsCi結構是T.125 MULTIPOINT-COMMUNICATION-SERVICE連接初始PDU,使用ASN.1 DER進行編碼。
•gccCrq結構是T.124 GCC(Generic Conference Control) ConnectData結構。

“Settings Data Block”是一個或多個“Settings Data Block”的串聯,其中每個具有以下格式:

存在各種類型的“Settings Data Block”,包括CS_CORE(0xC001),CS_SECURITY(0xC002),CS_NET(0xC003)等。
該tpktHeader字段具有下列結構:

tpktHeader中的所有多字節整數都是big-endian字節順序。version必須爲0x03,tpktLength指定整個數據包的長度。該漏洞與 “CS_NET”塊也稱爲clientNetworkData)有關。
該clientNetworkData字段包含請求的虛擬頻道列表。clientNetworkData字段的結構如下:

clientNetworkData的CS_NETHeader字段是0xC003,小端表示是\ x03 \ xc0。 channelCount字段指示請求的靜態虛擬通道。 channelNamen(其中n是1,2,...,N)字段定義了通道的8字節空終止名稱,channelOption_n字段指定了通道的屬性。
RDP協議支持靜態虛擬通道,旨在用作各種RDP組件和用戶擴展的通信鏈路。 這些通道以其8字節通道名稱而聞名,幷包括標準的Microsoft假設通道,如“rdpdr”(重定向),“rdpsnd”(聲音),“cliprdr”(剪貼板共享)等。用戶可以使用 RDP API支持其他渠道。 除上述通道外,Microsoft默認創建兩個通道:MS_T120(用於RDP本身)和CTXTW(用於Citrix ICA)。 客戶不應通過網絡創建這些渠道; 相反,當建立連接時,這些通道由Windows RDP系統在內部初始化。

使用termdd!IcaCreateChannel()創建通道,它首先檢查指定的命名通道是否存在,如果不是,則分配通道結構來創建通道。 指向通道結構的指針,這個指針我們稱爲ChannelControlStructure,它的結構存儲在一個表中,這個表我們稱爲ChannelPointerTable。 所有RDP連接都以ChannelPointerTable開頭,如下所示(前五個插槽不是用戶控制的,因此不顯示。而是將插槽號0作爲第一個客戶端可寫通道):

在上表中,每個槽都可以存儲一個ChannelControlStructure指針,其中標記爲Empty的存儲空指針。 當RDP客戶端通過在clientNetworkData中指定它們來連接和打開通道時,將創建相應的ChannelControlStructures,並將其指針存儲在從Slot 0開始的ChannelPointerTable中。請注意,CTXTW始終存在於插槽7中,而MS_T120存在於插槽0x1F中。

Microsoft Windows RDP內核驅動程序termdd.sys中存在“UAF”漏洞。在接收到包含clientNetworkData的“MCS Connect Initial and GCC Create”分組時,創建其中指定的信道的ChannelControlStructures。如果指定了名爲“MS_T120 \ x00”的通道(例如,在插槽10中),則termdd!IcaCreateChannel()調用termdd!IcaFindChannelByName()並返回由插槽0x1F中的MS_T120結構指向的ChannelControlStructure。該指針(與插槽0x1F中的指針相同)存儲在函數termdd!IcaBindVirtualChannels()中的用戶指定插槽(在此示例中爲插槽10)中。接下來,當使用MCS通道。加入請求打開通道時,MS_T120通道也會成功打開。如果攻擊者隨後將製作的數據發送到MS_T120頻道,則termdd.sys會嘗試通過發送錯誤消息並使用termdd!IcaCloseChannel()關閉該頻道來響應該消息,後者又調用termdd!IcaFreeChannel(),從而釋放 MS_T120 ChannelControlStructure並清除ChannelPointerTable中用戶控制的插槽(運行示例中的插槽10)中的指針。但是,插槽0x1F中的相同指針不會被清除。隨後,當連接終止時,調用RDPWD!SignalBrokenConnection(),然後調用termdd!IcaChannelInputInternal()並嘗試使用Slot 0x1F處的指針寫入釋放的ChannelControlStructure。這導致了“UAF”狀態。
遠程未經身份驗證的攻擊者可以通過在打開MS_T120通道時與目標服務器建立RDP連接並向其發送精心設計的數據來利用此漏洞。 成功利用將導致攻擊者能夠使用管理(內核級)權限執行任意代碼。

源代碼演示

以下代碼段取自termdd.sys版本6.1.7601.24056。 趨勢科技添加的評論已經突出顯示。

要檢測利用此漏洞的攻擊,檢測設備必須監視和解析分配端口上的流量,默認情況下爲3389 / TCP。
RDP連接以連接序列消息開始,由遠程桌面協議:基本連接和圖形遠程處理(MS-RDPBCGR)協議定義,如下所示:

注意:
•此檢測指南涉及消息“MCS Connect Initial和GCC Create”。
•RDP有兩種類型的加密:自定義RDP加密,一種使用TLS。在前一種情況下,“MCS Connect Initial和GCC Create”是純文本,而在後一種情況下,“MCS Connect Initial和GCC Create”是RDP客戶端在TLS建立後發送的第一個數據包。
•在交換第一個請求和響應之後,可以使用TLS加密流量。確定這一點的最簡單方法是檢查到服務器的第二個傳入數據包是否以“\ x16 \ x03”(TLS記錄類型和TLS客戶端Hello的高版本號)開頭。

檢測設備必須能夠檢查和分析RDP服務器與RDP客戶端之間的RDP通信。如果RDP通信使用TLS,則檢測設備必須在繼續執行後續步驟之前解密流量。
檢測設備必須查找傳入的“MCS Connect Initial and GCC Create”請求。“MCS Connect Initial and GCC Create”請求的結構如下:


除tpktHeader字段外,所有多字節整數都是小端字節順序。
•X.224層通常可以具有多種PDU類型並由任意長度組成,但“MCS Connect Initial和GCC Create”數據包具有3字節x224結構。
• mcsCi結構是T.125 MULTIPOINT-COMMUNICATION-SERVICE連接初始PDU,使用ASN.1 DER進行編碼。
•gccCCrq結構是T.124通用會議控制ConnectData結構.
“Settings Data Block”是一個或多個“Settings Data Block”的串聯,每個該塊具有以下格式:


存在各種類型的“Settings Data Block”,包括CS_CORE(0xC001),CS_SECURITY(0xC002),CS_NET(0xC003)等。

如果找到“MCS Connect Initial and GCC Create”請求,則檢測設備必須檢查每個“Settings Data Block”並查找類型爲CS_NET(0xC003)的設置。這樣的“Settings Data Block”稱爲clientNetworkData,具有以下結構:


如果找到clientNetworkData,則檢測設備必須遍歷每個channelName_n(其中n是1,2 ..,N)並檢查任何channelName n字段的值是否包含不區分大小寫的字符串“MS_T120”。如果找到這樣的頻道,則應將流量視爲惡意; 利用此漏洞的攻擊正在進行中

觸發漏洞

在將調試程序附加到目標系統時觸發漏洞時,會發生以下錯誤檢查:

結論

當Microsoft爲其支持的操作系統修補此漏洞時,他們決定還爲現在不支持的Windows XP和Windows Server 2003系統發佈補丁。這表明他們認爲這個漏洞有多嚴重。還有一些關於檢測到主動攻擊的討論,但毫無疑問這個漏洞的可利用性。此錯誤明顯獲得其關鍵評級,受影響的系統應儘快修補。對於那些仍然在Windows XP或Server 2003上的人來說,這是另一個提醒,要求制定升級計劃。微軟可能已經發布了針對此漏洞的補丁,但是每次發佈時,他們爲這些現在古老的系統發佈未來補丁的可能性會降低。
請注意,Microsoft補丁IcaBindVirtualChannels()和IcaReBindVirtualChannels()修復了termdd.sys中的兩個易受攻擊的函數。這兩個函數暴露了兩個不同但相似的攻擊向量。我們的分析側重於IcaBindVirtualChannels()公開的攻擊媒介。

特別感謝趨勢科技安全研究團隊的Richard Chen,Pengsu Cheng,Kamlapati Choubey和Saran Neti對此漏洞提供瞭如此全面的分析。我們當然希望將來可以看到更多的漏洞分析。

參考文獻:

[1] [MS-RDPBCGR]:Remote Desktop Protocol: Basic Connectivity and Graphics Remoting, https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-RDPBCGR/[MS-RDPBCGR].pdf
[2] Network-specific data protocol stacks for multimedia conferencing, ITU-T Recommendation T.123, https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.123-200701-I!!PDF-E&type=items
[3] Client Network Data (TS_UD_CS_NET), Microsoft, https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/49f99e00-caf1-4786-b43c-d425de29a03f
[4] T.125, Multipoint communication service protocol specification, ITU, https://www.itu.int/rec/T-REC-T.125
[5] T.124, Generic Conference Control, ITU, https://www.itu.int/rec/T-REC-T.124


 

0x00 前言

漏洞分析第一篇,在講UAF之前, 先簡單過一下HEVD代碼的邏輯,方便後續的分析。

0x01 HEVD代碼分析

1.1 驅動程序邏輯分析

驅動程序的主函數文件是HackSysExtremeVulnerableDriver.c
主要包含5個函數
DriverEntry: 驅動程序入口函數,類似於exe的main、dll的DllMain
IrpDeviceIoCtlHandler: 設備操作處理函數,DeviceIoControl請求處理,重要函數,根據請求會調用不同漏洞代碼模塊。
DriverUnloadHandler: 驅動卸載處理函數,可忽略
IrpCreateCloseHandler:驅動設備打開關閉處理函數,通常來說就是CreateFile、CloseHandle的請求處理,可忽略
IrpNotImplementedHandler:可忽略

除了DriverEntry是固定函數名,其他都是自定義的,只有傳參和返回類型是有要求的,那麼是怎麼將相關請求綁定相關函數的呢。
ring3在訪問驅動(通過驅動符號鏈接)進行操作時,會產生相應的IRP(I/O Request Package)事件,在驅動內對IRP進行操作,實現用戶層對驅動的操作。
實現對IRP事件的處理需要使用到派遣函數,這時就是通過驅動對象的MajorFunction屬性進行IRP請求類型和派遣函數綁定,所以派遣函數其實也是回調函數,也是爲啥傳參和返回類型是有要求的。
如下所示

IRP_MJ_CREATE對應CreateFile請求。
IRP_MJ_CLOSE對應CloseHandle請求。
IRP_MJ_DEVICE_CONTROL則對應DeviceIoControl請求,綁定的派遣函數是IrpDeviceIoCtlHandler。
IrpDeviceIoCtlHandler裏獲取IRP請求裏設置的控制碼,通過switch-case來調用不同的漏洞代碼模塊。當然控制碼是自定義的。

再來看一下exp裏如何訪問漏洞模塊的,像UAF,通過IOCTL_ALLOCATE_UAF_OBJECT和IOCTL_FREE_UAF_OBJECT等控制碼來訪問驅動裏的漏洞模塊。

對應到驅動代碼裏就是這塊分支函數,也是後續的分析重點。
我們需要理解的邏輯大概就這些了,關於更多驅動開發的知識可參考下面這個,可以快速掌握一些驅動知識。
https://bbs.pediy.com/thread-266038.htm

1.2 UAF漏洞代碼分析

1.2.1 UAF漏洞介紹

申請出一個堆塊保存在一個指針中,在釋放後,沒有將該指針清空,形成了一個懸掛指針(danglingpointer),而後再申請出堆塊時會將剛剛釋放出的堆塊申請出來,並複寫其內容,而懸掛指針此時仍然可以使用,使得出現了不可控的情況。攻擊者一般利用該漏洞進行函數指針的控制,從而劫持程序執行流。
當應用程序調用free()釋放內存時,如果內存塊小於256kb,dlmalloc並不馬上將內存塊釋放回內存,而是將內存塊標記爲空閒狀態。這麼做的原因有兩個:一是內存塊不一定能馬上釋放回內核(比如內存塊不是位於堆頂端),二是供應用程序下次申請內存使用(這是主要原因)。當dlmalloc中空閒內存量達到一定值時dlmalloc纔將空閒內存釋放回內核。如果應用程序申請的內存大於256kb,dlmalloc調用mmap()向內核申請一塊內存,返回返還給應用程序使用。如果應用程序釋放的內存大於256kb,dlmalloc馬上調用munmap()釋放內存。dlmalloc不會緩存大於256kb的內存塊,因爲這樣的內存塊太大了,最好不要長期佔用這麼大的內存資源。(這塊可能不太準確,大概看看就行)
但是其實這裏有以下幾種情況

  • 內存塊被釋放後,其對應的指針被設置爲 NULL , 然後再次使用,自然程序會崩潰。
  • 內存塊被釋放後,其對應的指針沒有被設置爲 NULL ,然後在它下一次被使用之前,沒有代碼對這塊內存塊進行修改,那麼程序很有可能可以正常運轉。
  • 內存塊被釋放後,其對應的指針沒有被設置爲 NULL,但是在它下一次使用之前,有代碼對這塊內存進行了修改,那麼當程序再次使用這塊內存時,就很有可能會出現奇怪的問題。

漏洞利用的過程可以分爲以下4步:

  1. 申請堆塊,保存指針。
  2. 釋放堆塊,形成懸掛指針。
  3. 再次申請堆塊,填充惡意數據。
  4. 使用懸掛指針,實現惡意目的。

下面我們去HEVD項目中具體看如何體現。

1.2.2 UAF漏洞代碼

漏洞代碼位於UseAfterFreeNonPagedPool.c
裏面包括4個重要函數以及這4個IRP處理函數,IRP處理函數會分別調用這4個重要函數

AllocateUaFObjectNonPagedPool

用於創建內核對象PUSE_AFTER_FREE_NON_PAGED_POOL
如下調用ExAllocatePoolWithTag在內核非分頁池申請內存,並填充數據

_USE_AFTER_FREE_NON_PAGED_POOL是一個0x58大小的結構體。

這邊將該結構體對象存放在全局變量g_UseAfterFreeObjectNonPagedPool中了

FreeUaFObjectNonPagedPool

用於釋放內核對象,這個函數裏有兩段代碼,上面會修復代碼,可以看到比g_UseAfterFreeObjectNonPagedPool被釋放後,多了一個置NULL的動作,這樣就可以防止懸掛指針的重利用。

AllocateFakeObjectNonPagedPool

將用戶模式傳入UserFakeObject指向內容拷貝給核心對象。
先根據FAKE_OBJECT_NON_PAGED_POOL結構體分配一個非分頁池的內存,然後將UserFakeObject的內容拷貝給KernelFakeObject。
這裏重點在於ExAllocatePoolWithTag分配內核池內存,如果存在一個已釋放的相同大小或者大一點的內存,那麼重新申請就有概率申請到該段內存,然後再爲該段內存寫入惡意代碼,這樣就會導致之前的懸掛指針再被調用時,訪問的是被覆蓋的內存內容,從而執行惡意代碼。
爲了增大申請到該段內存的概率,會使用一種池噴射的技巧,可參考擴展知識部分。

查看這個FAKE_OBJECT_NON_PAGED_POOL可以看到大小與之前的機構體一致。這個結構體沒有如上分成callback和buffer,這個其實不影響的,只要大小一樣,把結構體前4字節設置成惡意代碼地址即可。

UseUaFObjectNonPagedPool

該函數作用是調用全局變量g_UseAfterFreeObjectNonPagedPool,執行他的回調函數。

1.2.3 小結

分析了漏洞代碼,其實會對漏洞成因更加了解,我們只要按照AllocateUaFObjectNonPagedPool->FreeUaFObjectNonPagedPool->AllocateFakeObjectNonPagedPool->UseUaFObjectNonPagedPool的順序調用,就可以觸發漏洞,在第三步傳入包含惡意代碼地址的結構體,覆蓋原來的內存,再二次調用原來的結構體指針即可訪問惡意代碼,也就是UAF(use after free)的含義。
而修復方案在於Free之後將引用指針置位NULL,來避免二次訪問已釋放內存塊。
後續對於漏洞的利用除了上述流程還需要考慮如何提高申請到相同內存塊的機率,這個涉及到內核池管理,也用到內核池漏洞常用的池噴射技術。

0x02 漏洞利用

先在用戶空間的堆中分配FakeObject,將前4字節指向漏洞利用後運行的payload EopPayload地址。

2.1 池噴射代碼

再強調下爲啥需要池噴射,UAF需要重新申請到相同的內存塊並覆蓋成惡意代碼,而內核池中可能會有許多空間的內存塊,如果釋放的內存塊剛好和其他空閒的內存塊相鄰,系統就會將這兩個內存塊合併,那麼再申請內存時, 無法保證剛好用到我們釋放的那個內存塊。
NtAllocateReserveObject可用於在內核池分配兩個可選的內核對象,這裏是調用NtAllocateReserveObject在內核空間分配IoCompletionReserve內核對象,IoCompletionReserve的內核對象大小爲0x60,剛好比我們需要重利用的結構體0x58大一點。
池噴射第一步,先申請10000個IoCompletionReserve對象,用於將內核池中空閒、零散的池塊都申請完。
第二步,然後再申請5000個該對象,這時申請出來的池塊很大概率是連續的。

第三步,每隔一個內核對象釋放一個對象,這樣就會留下很多間隔0x60的空閒池塊,那麼在申請_USE_AFTER_FREE_NON_PAGED_POOL結構體時用到的池塊的前一個池塊就不會是空閒的,釋放的時候就不會被合併,這樣出意外的可能性就很低了。

這裏可能會有一個疑問,上面釋放了那麼多池塊,爲啥不會申請到其他,一個原因是申請是優先使用池塊大小相同或更相近的,我們在漏洞代碼裏看到的兩個結構體都是0x58是最相近的,另一個原因是越晚釋放的池塊會更優先被使用,也就是後入先出的概念。

2.2 UAF利用代碼

接着就是UAF利用的常規幾步,
第一步:訪問驅動,發送申請UAF_OBJECT結構體的請求。
第二步:訪問驅動,發送釋放UAF_OBJECT結構體的請求。

第三步:訪問驅動,發送申請FAKE_OBJECT結構體的請求,這裏循環了1000次,也是池噴射的概念,一次可能不一定申請到上面釋放的內存塊,所以增大概率,申請1000次。
第四步:也就是漏洞觸發惡意代碼執行的一步,調用原來已釋放結構體的懸掛指針,訪問被覆蓋的內存塊,觸發惡意代碼執行。
中間有一個FreeReserveObjects,用於釋放之前池噴射申請的所有內存塊,不然太佔用內存空間了,因爲運行在內核,不釋放的話即使你當前漏洞利用程序退出也不會釋放。

2.3 payload代碼

這段payload的作用是將SYSTEM進程的token複製到當前進程,這樣當前進程則爲system權限。

fs寄存器在Ring0中指向一個稱爲KPCR的數據結構,即FS段的起點與 KPCR 結構對齊,而在Ring0中fs寄存器一般爲0x30,這樣fs:[124h]就指向KPRCB數據結構的第四個字節。由於 KPRCB 結構比較大,在此就不列出來了。查看其數據結構可以看到第四個字節指向CurrentThead(KTHREAD類型)。這樣fs:[124h]其實是指向當前線程的_KTHREAD

kd> dt nt!_KPCR
   +0x000 NtTib            : _NT_TIB
    ......
   +0x0dc KernelReserved2  : [17] Uint4B
   +0x120 PrcbData         : _KPRCB


kd> dt _KPRCB
nt!_KPRCB
   +0x000 MinorVersion     : Uint2B
   +0x002 MajorVersion     : Uint2B
   +0x004 CurrentThread    : Ptr32 _KTHREAD
   +0x008 NextThread       : Ptr32 _KTHREAD
   +0x00c IdleThread       : Ptr32 _KTHREAD

_KTHREAD:[0x50] 指向 _KPROCESS, 即 nt!_KTHREAD.ApcState.Process_EPROCESS的第一個成員就是_KPROCESS,表示兩個數據結構地址一樣,則可以通過_KPROCESS訪問_EPROCESS數據
再來看看_EPROCESS的結構,+0xb8處是進程活動鏈表,用於儲存當前進程的信息,我們通過對它的遍歷,可以找到system的token(+0xf8),我們知道system的PID一直是4,通過這一點我們就可以遍歷了,遍歷到系統token之後替換就行了

kd> dt nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK
   +0x0a0 CreateTime       : _LARGE_INTEGER
   +0x0a8 ExitTime         : _LARGE_INTEGER
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF
   +0x0b4 UniqueProcessId  : Ptr32 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY
   +0x0c0 ProcessQuotaUsage : [2] Uint4B
   +0x0c8 ProcessQuotaPeak : [2] Uint4B
   +0x0d0 CommitCharge     : Uint4B
   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK
   +0x0dc PeakVirtualSize  : Uint4B
   +0x0e0 VirtualSize      : Uint4B
   +0x0e4 SessionProcessLinks : _LIST_ENTRY
   +0x0ec DebugPort        : Ptr32 Void
   +0x0f0 ExceptionPortData : Ptr32 Void
   +0x0f0 ExceptionPortValue : Uint4B
   +0x0f0 ExceptionPortState : Pos 0, 3 Bits
   +0x0f4 ObjectTable      : Ptr32 _HANDLE_TABLE
   +0x0f8 Token            : _EX_FAST_REF

2.4 執行

執行效果如下,效果是通過UAF在內核進行system token複製,讓當前進程的token已切換爲system,接着創建一個新進程如cmd.exe則也是system權限。

然後看下內核的變化

# 搜索HEVD
lm m H*
# 查看符號表
kd> x /D HEVD!u*
 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
9a3e77f2          HEVD!UaFObjectCallbackNonPagedPoolNx (void)
9a3e7806          HEVD!UseUaFObjectNonPagedPoolNx (void)
9a3e74e8          HEVD!UseUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
9a3e742c          HEVD!UseUaFObjectNonPagedPool (void)
9a3e78c2          HEVD!UseUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
9a3e7108          HEVD!UninitializedMemoryStackObjectCallback (void)
9a3e6fe6          HEVD!UninitializedMemoryPagedPoolObjectCallback (void)
9a3e70e8          HEVD!UninitializedMemoryStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
9a3e6fc6          HEVD!UninitializedMemoryPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
9a3e7418          HEVD!UaFObjectCallbackNonPagedPool (void)

通過上述操作可找到UseUaFObjectNonPagedPool函數的地址,然後分析該函數調用g_UseAfterFreeObjectNonPagedPool結構體的回調函數位置,定位到9a3e749b

在9a3e749b下斷點, 然後再運行exp

bp 9a3e749b

此處跳轉的內存地址是00ab39d0

再步入之前,我們先看下nonPagedPool,看池噴射的效果
g_UseAfterFreeObjectNonPagedPool保存着內核對象_USE_AFTER_FREE_NON_PAGED_POOL的地址

9a3e4014->8757b948

通過dd 8757b948可以看到,當前釋放的內核對象_USE_AFTER_FREE_NON_PAGED_POOL->CallBack已經指向ac39d0,後面連續的41(即A),其實這段就是FakeObject。
然後查看內核對象所在的nonPagedPool,這裏很明顯可以看到每個pool chunk大小都是60,並且每隔一個就是釋放的狀態,也正好符合我們剛纔池噴射的理論。

!pool 8757b948

最後一列TAG中,Hack即表示AllocateFakeObjectNonPagedPool調用分配給fakeObject的內存

我們繼續跟蹤步入該段代碼,可以看到和之前分析的payload一致,說明覆蓋懸掛指針的內存塊成功。

kd> !dml_proc
Address  PID  Image file name
86cf38a8 4    System         
.... 
88a8e460 368  HackSysEVDExpl

斷到最後token複製的位置,可以看到將system的token=0x8da01277拷貝給當前進程了。

kd> r ecx
ecx=88a8e460 #當前進程句柄
kd> r edx
edx=8da01277 #sytem進程的token
kd> dt nt!_EX_FAST_REF 86cf38a8+f8 # 通過句柄查看system進程的token
   +0x000 Object           : 0x8da01277 Void
   +0x000 RefCnt           : 0y111
   +0x000 Value            : 0x8da01277

0x03 擴展知識

3.1 windows API

3.1.1 DeviceIoControl

DeviceIoControl 將控制代碼直接發送到指定的設備驅動程序,使相應的設備執行相應的操作。
語法

BOOL WINAPI DeviceIoControl(
  _In_        HANDLE       hDevice,
  _In_        DWORD        dwIoControlCode,
  _In_opt_    LPVOID       lpInBuffer,
  _In_        DWORD        nInBufferSize,
  _Out_opt_   LPVOID       lpOutBuffer,
  _In_        DWORD        nOutBufferSize,
  _Out_opt_   LPDWORD      lpBytesReturned,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);
  • hDevice [in]
    需要執行操作的設備句柄。該設備通常是卷,目錄,文件或流,使用 CreateFile 函數打開獲取設備句柄。
  • dwIoControlCode [in]
    操作的控制代碼,該值標識要執行的特定操作以及執行該操作的設備的類型,每個控制代碼的文檔都提供了lpInBuffernInBufferSizelpOutBuffernOutBufferSize參數的使用細節。
  • lpInBuffer [in, optional]
    (可選)指向輸入緩衝區的指針。這些數據的格式取決於dwIoControlCode參數的值。如果dwIoControlCode指定不需要輸入數據的操作,則此參數可以爲NULL。
  • nInBufferSize [in]
    輸入緩衝區以字節爲單位的大小。單位爲字節。
  • lpOutBuffer [out, optional]
    (可選)指向輸出緩衝區的指針。這些數據的格式取決於dwIoControlCode參數的值。如果dwIoControlCode指定不返回數據的操作,則此參數可以爲NULL。
  • nOutBufferSize [in]
    輸出緩衝區以字節爲單位的大小。單位爲字節。
  • lpBytesReturned [out, optional]
    (可選)指向一個變量的指針,該變量接收存儲在輸出緩衝區中的數據的大小。如果輸出緩衝區太小,無法接收任何數據,則GetLastError返回ERROR_INSUFFICIENT_BUFFER,錯誤代碼122(0x7a),此時lpBytesReturned是零。
  • lpOverlapped [in, out, optional]
    (可選)指向OVERLAPPED結構的指針。

返回值:
如果操作成功完成,DeviceIoControl將返回一個非零值。
如果操作失敗或正在等待,則DeviceIoControl返回零。 要獲得擴展的錯誤信息,請調用GetLastError。
https://docs.microsoft.com/zh-cn/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol?redirectedfrom=MSDN

3.1.2 ExAllocatePoolWithTag

ExAllocatePoolWithTag用於內核模式,在中分配指定類型的池內存,並返回指向已分配內存空間的首地址的指針。

PVOID ExAllocatePoolWithTag(
  __drv_strictTypeMatch(__drv_typeExpr)POOL_TYPE PoolType,
  SIZE_T                                         NumberOfBytes,
  ULONG                                          Tag
);
  • PoolType
    該參數用來指定想要申請的內存的類型(內核空間中的內存主要分成兩類;分頁內存區,和未分頁內存區)。查詢可選的內存區類型可以到MSDN查詢POOL_TYPE結構。
    如果此值爲NonPagedPool,則分配非分頁內存。
    如果爲PagedPool, 則分配內存爲分頁內存。
  • NumberOfBytes
    通過該參數指定想要分配的內存的字節數,最好是4的倍數。
  • Tag
    爲將要被分配的空間指定標誌(就是給你得到的空間取個獨一無二的名字)。
    進一步解釋:賦給該參數的內容是一個字符串常量,最多可以包含四個字母,該字符串應該放到單引號當中(比如:‘tag1’‘tag2’)。另外,這個字符串常常是逆序的,如,‘1gaT’(所以大家會發現輸入這個參數的串確實都是倒過來的。。。)。輸入到這個參數中的每一個字符的ASCII值都必須在0-127之間。每次的申請空間的時候都最好應該使用一個獨一無二的標識,這樣可以幫助調試器和檢查器辨認和分析。
  • 返回值
    如果該函數發現目前系統的自由空間不足,就會返回NULL。否則,將返回指向被分配出來的空間的首地址。
    ### 3.1.3 ProbeForRead
    檢查用戶模式緩衝區是否確實駐留在地址空間的用戶部分中,並且是否正確對齊。簡而言之,就是看看這塊內存是否是Ring3的內存,並不檢查內存是否可讀。如果不存在ring3內存地址空間範圍內,則拋出異常。

    void ProbeForRead(
    const volatile VOID *Address,
    SIZE_T              Length,
    ULONG               Alignment
    );
  • Address
    [in] 指定用戶模式緩衝區的開始

  • Length
    [in] 指定用戶模式緩衝區的長度(以字節爲單位)
  • Alignment
    [in] 指定用戶模式緩衝區開頭所需的對齊方式(以字節爲單位)。
  • 返回值
    None

3.1.4 UNREFERENCED_PARAMETER

作用:告訴編譯器,已經使用了該變量,不必檢測警告!
在VC編譯器下,如果您用最高級別進行編譯,編譯器就會很苛刻地指出您的非常細小的警告。當你聲明瞭一個變量,而沒有使用時,編譯器就會報警告。

3.1.4 NtAllocateReserveObject

系統調用,負責在內核端創建保留對象–在內核池上執行內存分配,返回適當的Handle等

#define APC_OBJECT              0
#define IO_COMPLETION_OBJECT    1
#define MAX_OBJECT_ID           1
NTSTATUS STDCALL NtAllocateReserveObject(
    OUT PHANDLE hObject,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN DWORD ObjectType )
{
    PVOID       ObjectBuffer;
    HANDLE      hOutputHandle;
    NTSTATUS    NtStatus;
    if ( PreviousMode == UserMode )
    {
        /* Validate hObject */
    }
    if ( ObjectType > MAX_OBJECT_ID )
    {
        /* Bail out: STATUS_INVALID_PARAMETER
         */
    }else  {
        NtStatus = ObCreateObject( PreviousMode,
                       PspMemoryReserveObjectTypes[ObjectType],
                       ObjectAttributes,
                       PreviousMode,
                       0,
                       PspMemoryReserveObjectSizes[ObjectType],
                       0,
                       0,
                       &ObjectBuffer );
        if ( !NT_SUCCESS( NtStatus ) )
        /* Bail out: NtStatus
         */
            memset( ObjectBuffer, 0, PspMemoryReserveObjectSizes[ObjectType] );
        if ( ObjectType == IO_COMPLETION )
        {
            /*
             *
             * Perform some ObjectBuffer initialization
             *
             */
            ObjectBuffer[0x0C]  = 3;
            ObjectBuffer[0x20]  = PspIoMiniPacketCallbackRoutine;
            ObjectBuffer[0x24]  = ObjectBuffer;
            ObjectBuffer[0x28]  = 0;
        }
        NtStatus = ObInsertObjectEx( ObjectBuffer,
                         &hOutputHandle,
                         0,
                         0xF0003,
                         0,
                         0,
                         0 );
        if ( !NT_SUCCESS( NtStatus ) )
        /* Bail out: NtStatus
         */
            *hObject = hOutputHandle;
    }
    return(NtStatus);
}

hObject: 分配的內核對象句柄
ObjectType: 目前該參數只有兩個值0/1,用於標識兩個內核對象UserApcReserve和IoCompletionReserve,IoCompletionReserve對象大小爲0x60。

3.2 Preparing Pool Memory

翻譯自 Kernel Pool Exploitation on Windows 7 (BlackHat_DC_2011_Mandt_kernelpool-wp)
內核池利用的一個重要方面是能夠一致地覆蓋所需的內存。 由於內核池的碎片狀態使分配的位置無法預測,因此攻擊者必須首先使用內核對象或其他可控制的內存分配對內核池進行碎片整理。 在這方面的目標是分配所有空閒塊,以使池分配器返回一個新頁面。 用相同大小的分配填充新分配的頁面,並釋放第二個分配,這使攻擊者可以爲易受攻擊的緩衝區創建漏洞。 反過來,這將使攻擊者能夠溢出用於填充內核池的對象或內存分配。

3.3 池噴射

嘗試利用內核池漏洞時,必須處理塊(chunks)和池(pool)的元數據。如果你想避免藍屏,你需要控制一切,因爲在塊頭上會有一些額外的檢查。
內核池噴射是一項使池中分配位置可預測的藝術。這意味着你可以知道一個塊將被分配到哪裏,哪些塊在其附近。
如果您想要泄露某些精確的信息或覆蓋特定的數據,利用內核池噴射是必須的。
池噴射的基礎是分配足夠的對象,以確保您控制分配的位置。 Windows爲我們提供了許多在不同類型的池中分配對象的工具。例如,我們可以在NonPagedPool(非分頁池)中分配ReservedObjects或Semaphore 。關鍵是要找到與您要控制的池類型相匹配的對象。您選擇的對象大小也很重要,因爲它與創建後所留的空隙大小直接相關。一旦您選擇了對象,您將首先通過大量分配該對象使得池非隨機化。
上面申請的對象是屬於內核對象,針對內核漏洞的堆噴射,微軟有一個內核對象列表,我們可以通過調用用戶模式功能來創建內核對象,儘管它不是很完整。
https://msdn.microsoft.com/library/windows/desktop/ms724485(v=vs.85).aspx
有些細節仍然需要注意,否則可能會遇到麻煩:

  1. 如果您選擇的對象的大小不超過0x200字節,這很可能會在lookaside列表中存儲相應的釋放塊,這樣這些塊的不會被合併。爲避免這種情況,您必須釋放足夠多的對象填充滿lookaside列表。
  2. 您的釋放的塊可能會落在DeferredFree列表中,並且不會立即合併。所以你必須釋放足夠多的對象來填充滿這個列表,這樣才能釋放出塊製造空隙。
  3. 最後,你在池中分配對象,這對於整個內核是很常見的。這意味着您剛創建的空隙可能隨時被您無法控制的東西分配填充。所以你必須要快!

上述步驟的要點是:

  1. 通過使用對象的句柄,選擇需要釋放的塊
  2. 釋放足夠的塊填滿lookaside列表
  3. 釋放選定的塊
  4. 免釋放足夠的塊填充DeferredFree列表
  5. 儘可能快地使用你製造的空隙!

該技術實際應用中會有些改動。
先了解下UAF中的步驟
1.首先申請0x10000個該對象並將指針保存下來;
2.然後再申請0x5000個對象,將指針保存下來;
3.第二步中的0x5000個對象,每隔一個對象釋放一個對象;
第一步的操作是將現有的空餘堆塊都申請出來,第二步中申請出來的堆塊應該都是連續的,通過第三步的操作,使得我們申請UAE_AFTER_FREE結構體其前面的堆塊應該不是空閒的,因此在釋放的時候不會合並,從而再分配的時候出現意外的可能性基本爲0。

參考

Windows內核池噴射1-偏內核池介紹 https://www.anquanke.com/post/id/86188

3.4 內核對象

3.4.1 內核對象類型查詢

池噴射需要找到適合大小的內核對象
這裏使用windbg分析
首先,獲取更全面的對象列表

!object \ObjectTypes


這是一個可以在內核空間中分配的對象的列表。我們可以通過查看更多的細節來探索幾個關於它們的重要屬性。使用命令 dt nt!_OBJECT_TYPE _OBJECT_TYPE_INITIALIZER 結構的偏移量,它將給我們帶來極大的方便。讓我們看看它爲我們提供了 Mutant 對象的哪些我想要的信息:

dt nt!_OBJECT_TYPE 8521a838


然後閱讀下 _OBJECT_TYPE_INITIALIZER

dt nt!_OBJECT_TYPE_INITIALIZER 8521a838+28


_OBJECT_TYPE_INITIALIZER中有兩個關鍵信息
此對象被分配給的池類型 – 在這裏是非分頁池(NonPagedPool)
功能偏移(這在實際的漏洞利用部分十分重要)
上面就是可獲取到內核對象的一些信息,在實際過程中,分配到內核池的時候大小可能會有一些偏差。
像 用戶模式 CreateMutexA調用可在內核中創建Mutant對象,而未命名和命名的mutex大小是不一樣的,提供的名稱會佔用Mutant對象大小。

3.4.2 內核對象實際分析

接下來我們再測試下實際分配到內核池中時的大小怎麼查看。
如下運行一段代碼,會創建IoCompletionReserve內核對象,並保持不退出。
PS:這邊獲取到的句柄爲0x70

from ctypes import *
from ctypes.wintypes import *
kernel32 = windll.kernel32
ntdll = windll.ntdll
def alloc_iocreserve():
    IO_COMPLETION_OBJECT = 1
    hHandle = HANDLE(0)
    ntdll.NtAllocateReserveObject(byref(hHandle), 0x0, IO_COMPLETION_OBJECT)
    hHandle = hHandle.value
    if hHandle == None:
        print "[-] Error while creating IoCompletionReserve"
        return 0
    print "Handle: " + hex(hHandle)
    return hex(hHandle)
alloc_iocreserve()
variable = raw_input('Press any key to exit...')

搜索名稱爲python.exe的進程信息

kd> !process 0 0 python.exe
PROCESS 88bd0030  SessionId: 1  Cid: 0b00    Peb: 7ffd3000  ParentCid: 019c
    DirBase: bf2d0580  ObjectTable: 969c1de8  HandleCount:  40.
    Image: python.exe

切換到該進程上下文

kd> .process 88bd0030
ReadVirtual: 88bd0048 not properly sign extended
Implicit process is now 88bd0030
WARNING: .cache forcedecodeuser is not enabled

在當前上下文查詢句柄,其中可看到IoCompletionReserve內核對象的地址

kd> !handle 70
PROCESS 88bd0030  SessionId: 1  Cid: 0b00    Peb: 7ffd3000  ParentCid: 019c
    DirBase: bf2d0580  ObjectTable: 969c1de8  HandleCount:  40.
    Image: python.exe
Handle table at 969c1de8 with 40 entries in use
0070: Object: 8843a4b8  GrantedAccess: 000f0003 Entry: 8a2780e0
Object: 8843a4b8  Type: (86cf3be0) IoCompletionReserve
    ObjectHeader: 8843a4a0 (new version)
        HandleCount: 1  PointerCount: 1

這樣就可以找到池的位置,如下,每行是一個pool chunk,注意到帶*號的 pool chunk就是IoCompletionReserve內核對象在內核池塊中實際地址,並且可以看到大小爲0x60,根據這個大小我們就可以選取相應的內核對象進行池噴射了。

kd> !pool 8843a4b8
Pool page 8843a4b8 region is Unknown
 8843a000 size:   30 previous size:    0  (Allocated)  Mmdi
 8843a030 size:   18 previous size:   30  (Allocated)  MmSi
 8843a048 size:   30 previous size:   18  (Allocated)  Icp 
 8843a078 size:   18 previous size:   30  (Allocated)  MmSi
 8843a090 size:   68 previous size:   18  (Allocated)  EtwR (Protected)
 8843a0f8 size:   48 previous size:   68  (Allocated)  Vad 
 8843a140 size:   68 previous size:   48  (Allocated)  FMsl
 8843a1a8 size:   40 previous size:   68  (Allocated)  Even (Protected)
 8843a1e8 size:   20 previous size:   40  (Allocated)  ReTa
 8843a208 size:   50 previous size:   20  (Allocated)  Vadm
 8843a258 size:   c8 previous size:   50  (Allocated)  Ntfx
 8843a320 size:   48 previous size:   c8  (Allocated)  Vad 
 8843a368 size:   40 previous size:   48  (Allocated)  VM3D
 8843a3a8 size:   38 previous size:   40  (Allocated)  AlIn
 8843a3e0 size:   a8 previous size:   38  (Allocated)  File (Protected)
*8843a488 size:   60 previous size:   a8  (Allocated) *IoCo (Protected)
        Owning component : Unknown (update pooltag.txt)
 8843a4e8 size:   40 previous size:   60  (Allocated)  Even (Protected)

如果想看pool chunk具體信息,可如下,PreviousSize BlockSize在32位系統中是實際大小>>3,64位是>>4,所以這裏BlockSize=0xc*8=0x60,和上面獲取到的一致

kd> dt _POOL_HEADER 8843a488
nt!_POOL_HEADER
   +0x000 PreviousSize     : 0y000010101 (0x15)
   +0x000 PoolIndex        : 0y0000000 (0)
   +0x002 BlockSize        : 0y000001100 (0xc)
   +0x002 PoolType         : 0y0000010 (0x2)
   +0x000 Ulong1           : 0x40c0015
   +0x004 PoolTag          : 0xef436f49
   +0x004 AllocatorBackTraceIndex : 0x6f49
   +0x006 PoolTagHash      : 0xef43

3.4.3 參考

Windows內核池噴射1-偏內核池介紹 https://www.anquanke.com/post/id/86188
Windows內核池噴射2-合適的內核對象獲取 https://www.anquanke.com/post/id/86896
堆噴射 http://huntercht.51vip.biz:8888/zblog/?id=35

0x04 參考

https://gloomyer.com/2019/01/17/uaf-2019-1-17/
Windows Kernel Exploit 內核漏洞學習(1)-UAF https://bbs.pediy.com/thread-252310.htm
https://www.freesion.com/article/9516423134/
Windows堆噴射 https://www.anquanke.com/post/id/85586
Windows堆噴射 https://www.anquanke.com/post/id/85592
內核池塊分析: https://www.cnblogs.com/flycat-2016/p/5449738.html
windows 內核池原理: https://blog.csdn.net/qq_38025365/article/details/106259634
內核池利用文檔 BlackHat_DC_2011_Mandt_kernelpool-wp.pdf


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章