<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【經典回顧系列】 一步一步教你漏洞挖掘之Windows SMB Ghost CVE-2020-0796(三)

    VSole2021-10-07 10:15:16

    接上文:

    漏洞利用之遠程命令執行(二)

    0x01 實現任意地址讀

    遠程協議的信息泄露很難實現,關鍵是漏洞能夠破壞或改寫響應報文,利用協議中返回數據的相關函數將信息發回給用戶。而目前來看,我們原始的漏洞只能破壞請求報文的數據結構,值得慶幸的是我們通過該漏洞已經獲取了任意地址寫的能力。正常情況下,我們會利用任意地址寫來改寫一些關鍵數據結構,來破壞響應數據結構,實現信息泄露,問題是我們無法得知往哪寫。

    注意到當用戶建立新的SMB連接時,在`Smb2ExecuteNegotiateReal`中會申請用于響應的數據結構(與請求包一樣的`SRVNET_BUFFER_HDR`,其中的MDL結構中包含一個重要的用于存放返回數據的物理內存地址),該函數最終也是用`SrvNetAllocateBuffer`來申請內存:

    幸運的是`SrvNetAllocateBuffer`在內存分配時系統使用了`Lookaside` 列表(參考前面`SrvNetAllocateBuffer`的代碼),這類似于一種緩存,可以加快內存的申請和釋放速度(`ExAllocatePoolWithTag`及`ExFreePoolWithTag`都會花費大量時間),該列表中的內存塊在申請和釋放時都不會對內容進行初始化(對于這一點,個人覺得會存在內存未初始化的風險)。

    經過初步分析,利用`SrvNetAllocateBuffer`申請的內存,只有最終進入`SrvNetAllocateBufferFromPool`才會對內存進行初始化操作,否則利用`Lookaside`獲取的內存將保持已有的數據,后面在SMB 協商階段將被直接使用。因此,如果恰好能夠從`Lookaside` 列表申請到之前釋放的經過精心構造的數據結構,就能使`Nogotiation`的返回包讀取任意地址的內容,發生信息泄露。

    現在我們來捋一下思路,要實現任意地址讀,可以通過以下幾個步驟來實現:

    1. 在本地準備好偽造的MDL結構,重點是將物理地址字段配置為想要讀取的地址
    2. 利用任意寫,在`SystemSharedPage`(固定地址0xfffff78000000000)中寫入一個偽造的MDL結構
    3. 利用緩沖區溢出,將`SRVNET_BUFFER_HDR+0x38`的指針改為偽造的MDL地址,關閉連接將釋放內存
    4. 發起正常的SMB 協商請求,如果`SRVNET_BUFFER_HDR`復用成功,將返回指定物理地址處的數據
    5. 利用PML4泄露內存頁表內容,通過虛實轉換實現任意地址讀原語

    (1)偽造MDL結構

    關于MDL的具體結構,微軟有對應的文檔:

    MDL (wdm.h)
    https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_mdl

    也有相關的使用研究:

    Windows MDL原理總結
    https://www.cnblogs.com/jack204/archive/2011/12/25/2300983.html
    struct _MDL {    struct _MDL      *Next;           // 0x0    CSHORT           Size;            // 0x48    CSHORT           MdlFlags;        // 0x5018    ULONG            Processor;       // 0x0    PVOID            MappedSystemVa;  // 0xfffff78000000800    PVOID            StartVa;         // 0xfffff78000000000    ULONG            ByteCount;       // 0x258    ULONG            ByteOffset;      //     PFN_NUMBER       physMem[];       // 頁表項 pfn} MDL, *PMDL;
    

    計算物理地址對應的頁表項:

    pfn = (phys_addr & 0xFFFFFFFFFFFFF000) >> 12
    

    (2)將偽造的MDL寫入SystemSharedPage

    為了減少其他數據的干擾,我們將MDL盡量存放在`SystemSharedPage`的后端。

    phys_addr = 0x222222                # 計劃讀取的目標物理地址pmdl_mapva = SystemSharedPage+0x800 # MDL結構體中的MappedSystemVapmdl_va = SystemSharedPage+0x900    # MDL結構的存放位置fake_mdl = MDL(pmdl_mapva, phys_addr).raw_bytes()write_primitive(args.ip, args.port, fake_mdl, pmdl_va)
    

    發送攻擊數據包后,在`SystemSharedPage`中成功寫入了偽造的MDL:

    (3)篡改請求數據結構中的pMDL指針

    在寫原語中,我們利用了兩次溢出實現了任意地址寫(第一次溢出將目標地址改為指定值,第二次溢出將指定數據寫入指定位置)。這里我們并不需要第二次溢出,只要在第一次溢出時直接將pMDL篡改為指定值就行。

    該過程看似直接,其實這里有一個問題:由于pMDL位于`SRVNET_BUFFER_HDR`的后面,如果直接使用之前的溢出方法,將導致整個`SRVNET_BUFFER_HDR`都被覆蓋(其中包括一個內存指針`header->pNonPagedPoolAddr`),這將導致后續的處理發生異常(釋放時調用`ExFreePoolWithTag`)。

    【解決該問題】我們需要跳過`SRVNET_BUFFER_HDR`,直接溢出篡改pMDL。因為`SmbCompressionDecompress`中會將解壓的數據拷貝至`UserBuffer + Offset`處,所以我們只要將Offset設置為pMDL所在偏移就可以實現。

    第二個問題:如果繼續執行,在溢出點3進行內存拷貝時,`SRVNET_BUFFER_HDR`依然會被破壞,導致`header->pNonPagedPoolAddr`被篡改,最終`ExFreePoolWithTag`異常。

    【解決該問題】我們要避免程序流程進入溢出點3,那就只能使`SmbCompressionDecompress`出錯,后面直接退出:

    NTSTATUS Status = SmbCompressionDecompress(...);if (Status < 0 ||     FinalCompressedSize != Header->OriginalSize) {    SrvNetFreeBuffer(Alloc);     return STATUS_BAD_DATA;}// ...// 避免執行至溢出點3
    

    從IDA中簡單分析`SmbCompressionDecompress`,判斷只要在解壓縮過程中發生異常,應該就能夠使其返回錯誤值:

    我們嘗試在構造壓縮數據時,故意將后面改為錯誤結構,在數據解壓過程中觸發異常,導致`RtlDecompressBufferEx2`返回錯誤,而`RtlDecompressBufferEx2`在發生錯誤前,仍然會將已經解壓成功的數據拷貝至目標位置。利用這個特性我們能夠篡改`SRVNET_BUFFER_HDR`中的pMDL指針,同時又保證程序不會破壞`header->pNonPagedPoolAddr`。

    def compress_evil(buf, chunk_size=0x1000):    out = b""    while buf:        chunk = buf[:chunk_size]        compressed = _compress_chunk(chunk) # 正常壓縮過程        flags = 0xB000 # 始終標記為壓縮狀態        header = struct.pack(', flags | (len(compressed)-1))        out += header + compressed        buf = buf[chunk_size:]            out += struct.pack(', 0x1337) # 破壞 "next" 塊    return out
    # 該函數能夠篡改`SRVNET_BUFFER_HDR`中指定偏移處的內容# offset = 0x38 時指向pMDLdef write_srvnet_buffer_hdr(ip, port, data, offset):    sock = reconnect(ip, port)    smb_negotiate(sock)    sock.recv(1000)    compr_data = compress_evil(data) # 構造有問題的壓縮數據    dummy_data = b"\x33"*(0x1100 + offset)    smb_compress(sock, compr_data, 0xFFFFEFFF, dummy_data)    sock.close()
    

    馬上測試一下,我們在發送攻擊數據包前,先看一下`SRVNET_BUFFER_HDR`中的內容:

    然后在`SmbCompressionDecompress`函數返回后再看一下`SRVNET_BUFFER_HDR`中的內容:

    可以看到`SRVNET_BUFFER_HDR->pMDL`已經被篡改為`0xfffff78000000900`,就是我們自己偽造的MDL結構,其他數據內容都保持不變。返回值為`0xc0000242`,繼續執行后將釋放內容并返回,從而避免進入溢出點3導致系統崩潰。

    (4)再次建立SMB連接,嘗試任意物理內存讀取

    再次建立正常的SMB連接就行,根據前面所說,在協商階段會申請一個`ResponseBuffer`:

    系統會嘗試用`Lookaside`表查詢可用的內存塊,如何幸運的話(實際測試發現并不一定每次都成功,但是攻擊過程并不會導致目標崩潰,因此可以反復嘗試)應該能夠申請到上次連接釋放的`SRVNET_BUFFER_HDR`,就像下面的情況:

    直接繼續執行,我們就能收到包含泄露的內存數據的響應包了,可以對比下wireshark和實際內存的內容:

    綜上4步,任意物理內存讀取達成!

    (5)泄露內存頁表,實現任意虛擬地址讀

    首先我們先研究下Windows的內存機制:

    Windows的內存機制
    https://blog.csdn.net/ratonsea/article/details/106842622

    在64位操作系統上的內存分頁使用4級頁表,將物理頁面映射到虛擬頁面,它們分別是PML4(也就是PXE)、PDPT、PD和PT。控制寄存器CR3包含當前進程PML4表的(物理)內存基地址。

    【注】這里由于Windbg Preview不支持`!vtop`等命令,只能切換回老的Windbg!!!

    根據 Ricera Security 的研究,PML4似乎并沒有實現隨機化,我在vmware的虛擬機中查看寄存器cr3的值,確實如其所說為固定值:`0x1ad000`,需要注意的是針對不同的引導方式PML4會有不同取值:`0x1aa000(BIOS)`或者`0x1ad000(UEFI)`。根據這點,我們能夠通過dump頁表信息,實現虛擬地址和物理地址的轉換,最終將虛擬地址的讀取轉換成物理地址的讀取。

    在實際的利用代碼中,Ricerca Security使用了一種似乎更為通用的方式。從物理地址`0x1000`處開始進行搜索,通過比對特征數值來尋找PML4地址和HAL堆地址:

    # Use lowstub jmp bytes to signature searchLOWSTUB_JMP = 0x1000600E9# ...index = 0x1000buff = read_physmem_primitive(ip, port, index)entry = struct.unpack(", buff[0:8])[0] & 0xFFFFFFFFFFFF00FFif entry == LOWSTUB_JMP:    PML4 = struct.unpack(", buff[0xA0:0xA8])[0]    print("[+] PML4 at %lx" % PML4)        PHAL_HEAP = struct.unpack(",                 buff[0x78:0x80])[0] & 0xFFFFFFFFF0000000    print("[+] base of HAL heap at %lx" % PHAL_HEAP)    return
    

    但是實際測試過程中Ricerca Security的利用代碼一直未能成功。測試發現對部分物理地址讀取會超時(原因有待分析),自己對代碼做了一些修改(需要增加對`sock.recv`的異常處理,防止程序超時退出),終于成功泄露PML4的物理地址:

    后面就是要利用PML4的地址來獲取各級頁表內容,實現內存轉換。但是在內存讀取過程中依然存在讀取失敗的問題:就是上面提到的讀取特定物理地址失敗的問題,比如讀取PML4的地址`0x1ad000`時返回的數據只有6個`0x00`:

    分析問題原因:通過跟蹤數據包的構造和發送過程,系統最終通過`MmMapLockedPagesSpecifyCache`函數根據MDL將物理地址映射為虛擬地址。

    bp nt!MmMapLockedPagesSpecifyCachebp srvnet!SrvNetSendData ".if(poi(@rdx+8)==0xfffff78000000900){}.else{gc;}"bp nt!MiFillSystemPtes+0x29d
    

    初步分析是`MmMapLockedPagesSpecifyCache`返回NULL導致的問題,這一點非常奇怪,比如我讀0x1000處就能夠map成功,而對于0x2000就不行。

    進一步在`MmMapLockedPagesSpecifyCache`中分別調用了`MiReservePtes`和`MiFillSystemPtes`兩個函數,其中`MiReservePtes`申請一個虛擬地址用于內存映射,`MiFillSystemPtes`實現物理內存映射(虛擬地址保存在rdi)。

    調試發現`MiFillSystemPtes`會對映射的內存頁進行檢查,如果地址位于頁表本身所在地址范圍,將導致內存映射失敗。(IDA中的地址與調試器中的不一樣,應該是頁表地址隨機化造成的)。

    也就是說頁表空間中的所有地址都無法通過`MmMapLockedPagesSpecifyCache`進行映射(系統版本:Windows10 1909 18363.418)。

    【問題】目前還沒有找到解決方案。未完待續!!

    0x02 突破DEP防護

    通過任意地址寫,修改內存頁對應的PTE表項,清除NX bit位,使目標內存頁改為可執行。

    0x03 截獲程序控制流(控制RIP)

    利用任意地址寫,篡改內存中的某個指針,進入內存態shellcode。

    0x04 突破控制流防護(CFG)

    用戶態CFG可能會攔截shellcode的執行,可以在內核態中patch `ntdll!LdrpValidateUserCallTarget`來繞過。

    補充

    【說明】ricerca security的研究人員在srv2模塊中發現了一個函數`Srv2SetResponseBufferToReceiveBuffer`,該函數將請求數據結構直接賦值給響應數據結構,也就是說我們破壞的請求數據結構將直接影響響應數據。此外利用IDA可以發現,`srv2!Smb2SetError`函數會調用`srv2!Srv2SetResponseBufferToReceiveBuffer`,也就是說當srv2.sys想發送錯誤消息時就會調用該函數。但是在漏洞利用過程中,似乎并不需要利用這點。

    void Srv2SetResponseBufferToReceiveBuffer(    SRV2_WORKITEM *workitem) {  ...  workitem->psbhResponse = workitem->psbhRequest;  ...}
    

    寫在最后

    此處,CVE-2020-0796漏洞的分析告一段落了,其實還有很多問題沒有搞清楚,有些許遺憾,值得進一步深入研究,有志同道合的小伙伴可以一起探討。

    漏洞挖掘mdl
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    引言B***是一款基于.NET WebForm開發的計費軟件,近日看到網上曝光B***存在SQL注入漏洞CVE-2021-4***,結合xp_cmdshell可以實現RCE。
    【經典回顧系列】 Windows SMB Ghost CVE-2020-0796漏洞分析與利用(三)
    Windows SMB Ghost CVE-2020-0796漏洞分析與利用(二)
    CVE-2021-24086漏洞分析
    2022-07-19 16:41:30
    漏洞信息2021年,Microsoft發布了一個安全補丁程序,修復了一個拒絕服務漏洞,編號為CVE-2021-24086,該漏洞影響每個Windows版本的IPv6堆棧,此問題是由于IPv6分片處理不當引起的。
    0x01 確定目標無目標隨便打,有沒有自己對應的SRC應急響應平臺不說,還往往會因為一開始沒有挖掘漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。所以在真的想要花點時間在SRC漏洞挖掘上的話,建議先選好目標。0x02 確認測試范圍前面說到確定測什么SRC,那么下面就要通過一些方法,獲取這個SRC的測試范圍,以免測偏。
    漏洞挖掘工具—afrog
    2023-03-20 10:20:07
    -t http://example.com -o result.html2、掃描多個目標 afrog -T urls.txt -o result.html例如:urls.txthttp://example.comhttp://test.comhttp://github.com3、測試單個 PoC 文件 afrog?-t http://example.com -P ./testing/poc-test.yaml -o result.html4、測試多個 PoC 文件 afrog?
    但又沒登錄怎么獲取的當前用戶的Access-Reset-Ticket真相只有一個,看看接口哪里獲取到的原來是在輸入要找回的用戶就會獲取當前用戶的Access-Reset-Ticket6到了,開發是我大哥嘗試修改可行,修改管理員賬號,然后起飛下機。漏洞已修復,廠商也修復了漏洞更新到了最新版本。
    漏洞挖掘是指對應用程序中未知漏洞的探索,通過綜合應用各種技術和工具,盡可能地找出其中的潛在漏洞。cookie的key為RememberMe,并對相關信息進行序列化,先使用aes加密,然后再使用base64編碼處理形成的。在網上關于Shiro反序列化的介紹很多,我這里就只簡單介紹一下,詳情各位可以看下大神們對其源碼的分析。
    這里建議doc文檔,圖片可以貼的詳細一些。爆破完好了,一樣的6。想給它一個清晰完整的定義其實是非常困難的。
    一、漏洞挖掘的前期–信息收集 雖然是前期,但是卻是我認為最重要的一部分; 很多人挖洞的時候說不知道如何入手,其實挖洞就是信息收集+常規owasp top 10+邏輯漏洞(重要的可能就是思路猥瑣一點),這些漏洞的測試方法本身不是特別復雜,一般混跡在安全圈子的人都能復現漏洞。接下來我就著重說一下我在信息收集方面的心得。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类