這是本系列文章中的下篇,我們將繼續與讀者一道,深入分析數據擦除型的惡意軟件HermeticWiper。
(接上文)
禁用影子副本刪除影子副本是勒索軟件的一個常見行為。它應該是為了破壞系統備份,並使恢復工作陷入癱瘓。就本例來說,我們可以看到該樣本禁用了影子副本服務。
影子副本被禁用
數據碎片化在我們的分析過程中,我們注意到惡意軟件會將磁盤上的文件碎片化(與碎片整理相反)。
在運行碎片化例程之前,它會更改與資源管理器相關的一些設置:
更改註冊表,使發現NTFS操作更加困難
這可能是為了隱藏關於文件狀態的信息,使得它們更加難以被發現:
下面的函數顯示了碎片化例程的執行過程:
用於實現數據碎片化的包裝器函數
其中,標準windows目錄被排除在外:
將被跳過的文件夾列表
這樣做既可以節省時間(不破壞標准文件),又可以避免影響系統穩定性。
文件碎片化過程如下所示:
碎片詳細信息(1)
碎片詳細信息(2)
數據碎片化算法的實現,是通過將不同的IOCTL_CODES(FSCTL)用作FSCTL_GET_RETRIEVAL_POINTERS和FSCTL_GET_MOVE_FILES來實現的。該代碼看起來與碎片整理代碼非常相似。但就這裡來說,修改的目的是為了實現碎片化,即將文件塊分割為碎片,並將其移動到磁盤中的空閒簇中。
數據收集在這些準備工作完成之後,該惡意軟件就會進入執行的第二個階段:數據收集。在各種勒索軟件案例中,我們經常會看到這種情況:在加密之前,惡意軟件會遍歷目錄,並列出要攻擊的文件清單。該惡意軟件的情況與此類似,但更有趣的是,因為該軟件在進行目錄遍歷的時候,使用的不是windows API,而是NTFS文件系統,來讀取各種結構體並手動解析它們。為此,該惡意軟件需要通過標準的Windows設備來發送IOCTL(新安裝的驅動程序尚未使用)。
數據存儲上述解析過程的結果被存儲在我們設法重建的自定義結構體中,其定義如下所示:
structelemStr
{
elemStr*fLink;
elemStr*bLink;
chunkStr*chunkPtr;
DWORDdiskNumber;
BYTE*randomBufToWrite;
DWORDsizeBuffer;
};
structchunkStr
{
chunkStr*fLink;
chunkStr*bLink;
LARGE_INTEGERoffset;
QWORDchunk_size;
};就像您看到的那樣,它們都是鍊錶。
第一個elemStr定義了將被覆蓋的元素。之後,代碼會檢索其大小,並生成專用於覆蓋它的隨機緩衝區:
生成的隨機數據
在這裡,“chunk”表示要覆蓋的物理地址的連續塊。
因此,一般來說,惡意軟件將在後面的兩個步驟中用到這些結構體。第一步是收集所有數據。第二步是使用前面創建的結構體來擦除數據。
收集相關元素正如之前所看到的,這些結構體將被發送給執行數據破壞的函數。以下是以後需要銷毀的元素。
惡意軟件自身的可執行文件和植入的驅動程序我們已經看到,攻擊者對清理自己的踪跡很感興趣。為了達到這個目的,他們會從磁盤上刪除自己的可執行文件,即使二進製文件本身一直在運行並在內存中。正如HermeticWiper在文件系統中執行的任何其他任務一樣,刪除自己的二進製文件的方式與其他惡意軟件略有不同。攻擊者首先設法找到二進製文件在原始文件中的偏移量,最後,他們將覆蓋這個特定的偏移量。
HermeticWiper文件將被銷毀,同時被銷毀的還有其他元素
被植入的文件(壓縮和未壓縮的驅動程序)將被添加到同一結構體中,該動作是在在安裝之後完成的。
引導扇區攻擊者的目的之一,就是使設備無法加載操作系統。接下來的第一步,就是枚舉所有物理設備以及分區。為此,他們使用循環語句來嘗試打開harddisk[num]的句柄,其中num將從0迭代到100:
這個循環語句展示了攻擊者是如何從HardDisk0迭代到HardDisk100的
然後,所有這些信息都被存儲到一個elemStr結構體中,該結構體包含作為磁盤號的數據。在本例中,chunkElement將描述引導扇區的原始地址。在這裡,需要特別關註一下C:\System Volume Information。另外,攻擊者將在boot_sections結構體中添加以下文件夾內容:
對parse_NTFS_AND_execute_callback函數的調用
根據Microsoft的說法,“Mount Manager維護每個NTFS卷上的Mount Manager遠程數據庫,其中Mount Manager記錄了為該卷定義的任何掛載點。數據庫文件駐留在“NTFS卷上的目錄系統卷信息”(詳見“Windows Internals, 6th edition”)中。所以,這個技術也是為了提高破壞能力而創造的。最後,利用EasyUS驅動程序,所有這些收集到的偏移量都將像惡意二進製文件一樣被覆蓋掉。
保留扇區與MFT和以前一樣,惡意軟件將再次對物理驅動器ID進行暴力破解,以找到有效的驅動器ID。然後,它會使用IOCTL_DISK_GET_DRIVE_LAYOUT_EX檢索有關驅動器上所有主分區的信息,並從相應分區讀取第一個扇區。接著,通過ioctl_disk_get_drive_geometry_ex來獲取讀取磁盤的第一個扇區所需的其他信息。
檢索每個磁盤的相關信息
一旦讀取了分區的第一個扇區,該軟件就會在該扇區上調用惡意軟件傳遞的回調函數。
之後,它會根據文件系統的類型進行相應的處理:如果是FAT類型,那麼它會擦除所有保留扇區,而FAT文件系統中的引導記錄扇區是保留扇區的一部分;如果是NTFS類型,該惡意軟件就會清除磁盤上存在的MFT和MFTMirror(備份MFT),其目的是使數據恢復更加困難。
用於處理FAT文件系統的例程
用於處理NTFS文件系統的例程
NTFS卷上的每個文件都是由名為主文件表(MFT)的特殊文件中的記錄來表示的。在MFT被破壞的情況下,則可以通過讀取MFT鏡像來恢復原始MFT,其第一記錄與MFT的第一記錄相同。 MFT表為文件系統提供了相應的索引,並提供了文件駐留位置等信息。如果沒有MFT,系統將無法知道有哪些文件夾和文件,以及修改日期等信息。
Bitmap和日誌文件為了阻止數據被恢復,惡意軟件還經常會覆蓋所有邏輯驅動器上的Bitmap和日誌文件。就本例來說,邏輯驅動器是通過GetLogicalDriveStringsW進行檢索的。另外,這些結構體在進行數據恢復和取證調查時也很重要。實際上,$bitmap保存了空閒簇和已用簇的相關信息,而$logfile則保存了文件系統中發生的事務日誌。
此外,用戶文件也會受到數據破壞的影響。我們發現該惡意軟件還會覆蓋C:/Documents and settings文件夾中的所有內容。在現代Windows系統中,該文件夾將指向C:/Users。這個文件夾存放的是用戶的數據文件夾(例如,我的文檔或桌面)。在這個過程中,有些文件會被跳過,比如APPDATA下的文件,但一般來說,這些文件夾下的所有文件都會被覆蓋。
收集需要擦除的簇數據收集的最後一步,就是獲取清除磁盤上所有已佔用的簇所需的信息。為了獲取這些信息,惡意軟件使用了FSCTL_GET_VOLUME_BITMAP IOCTL來獲取磁盤上所有已佔用和空閒簇的相關信息。該惡意軟件會遍歷所有的邏輯磁盤,並使用FSCTL_GET_VOLUME_BITMAP檢索bitmap,而bitmap中的每一位表示一個簇,值1表示該簇已被佔用,0表示該簇處於空閒狀態。對於通過IOCTL檢索到的bitmap,該惡意軟件將對其進行逐位遍歷,然後,將所有已經佔用的簇添加到前文描述的擦除結構體中。這裡要注意的一點是,該惡意軟件匯集了所有相鄰的簇,而這些相鄰的簇是由單個chunk結構表示的——這一點與之前的表示方法不同,那時用一個chunk結構表示單個簇。
最後,所有已佔用的簇將被收集到一個elemStr類型的結構體中,以便銷毀。
這一切是如何進行的?到目前為止,我們已經知道一些NTFS屬性(如屬性、索引等)是用來收集數據的,之後這些數據將被銷毀。接下來,我們將展示一個例子,說明攻擊者是如何實現這一功能的,並展示其複雜程度。
為此,我們將以負責收集Windows日誌文件的代碼為例進行介紹:
負責收集Windows日誌文件的代碼
在調用上述代碼之後,會填充一些數據結構,其中包含有關物理磁盤屬性和文件夾名稱本身的數據。我們對NTFS文件系統的第一次引用是在檢索HANDLE的過程中發現的。這個文件夾是作為NTFS流打開的:
用於默認目錄流的HANDLE
其中,第一個調用將解析$INDEX_ROOT屬性,其功能與第二個調用相對類似,也更簡單,在第二個調用中使用了$INDEX_ALLOCATION屬性。關於這些NTFS屬性的其他信息可以在這裡找到。我們將假設元素列表足夠長,比如$INDEX_ALLOCATION,我們將深入考察這個調用:
相關的回調函數
為了更好地理解整個過程,我們需要記住發送的參數。其中,前面的兩個參數(nFileIndexLow和nFileIndexHigh)用於調用函數FSCTL_GET_NTFS_FILE_RECORD,它將檢索到一條NTFS記錄。在進行一些檢查之後(例如,檢查magic值),我們將調用一個名為callback_when_attribute_is_found的函數。注意,發送給這個函數的第一個參數將是之前發送的值$INDEX_ALLOCATION(0x20)。
調用callback_when_attribute_is_found函數
這個函數將遍歷作為記錄一部分的所有NTFS屬性。為此,代碼必須找到第一個屬性的偏移量。這個偏移量的長度只有2個字,因為這個偏移是相對於結構體而言的。 NTFS記錄頭部的佈局如下所示:
NTFS記錄頭部的佈局
一個NTFS文件記錄的結構如下所示:
記錄頭部
屬性
屬性
屬性NTFS記錄的佈局
如果我們還記得$INDEX_ALLOCATION(0x20),事情就很容易理解了。這些屬性將以一個特定的TypeCode開始,就像$INDEX_ALLOCATION那樣。因此,如果其中一個屬性與所需的選定類型相匹配,第一個回調函數將被觸發。
用於匹配屬性和回調函數的代碼
如果沒有匹配的TypeCode,但發現了$ATTRIBUTE_LIST,則將意味著存在更多的屬性,但這些屬性不適合$MFT表。在這種罕見的情況下,該惡意軟件將繼續處理這些額外的屬性,並將遞歸地調用第一個函數。
讓我們看看這個回調函數會做些什麼。記住,這個回調函數,在我們的案例中是indexAllocation_Callback_CollectAllfiles。第一步,是恢復這個屬性所指向的流。由於$INDEX_ALLOCATION是一個用於目錄的屬性,所以這個流可以是一個索引數組(塊索引):
使用原始磁盤偏移量恢復的塊索引數組
由於這是一個索引數組,這些索引將指向某個東西。正如你所想像的,這個東西就是NTFS記錄。在原始磁盤中,這些類型的索引看起來如下所示:
在原始磁盤鏡像文件中發現的索引塊的例子
由於索引指向記錄,因此,所有這些記錄將被遞歸地發送到初始函數。但是這一次的回調函數將是不同的,類型碼也是不同的:
調用$DATA回調函數
所以這一次,每條發送的記錄都會有不同的表現:將尋找$DATA屬性,而不是$INDEX_ALLOCATION($DATA包含文件數據)。另外,執行的回調函數將是不同的(現在命名為dataExecuting)。通過使用第一次調用中發送的磁盤屬性,結合從索引中收集的信息,這個回調函數將定位文件在磁盤中的確切位置。最後,這些文件就像我們在本報告中總結的所有文件一樣,被作為成員加入到elemStr*結構體中。如上所述,該結構體中包含的偏移量處的內容,將在最後的步驟中被惡意軟件所覆蓋。
調用函數,將文件的偏移量添加到elemStr類型的結構體中,以便以後銷毀數據
覆蓋數據最後,在收集完所有數據後,惡意軟件就開始執行覆蓋操作。為此,它會將elemStr結構體傳遞給該函數,以處理鍊錶上的所有元素:
to_overwrite_collected_sections函數
執行覆蓋操作的函數首先通過前面安裝的驅動程序來獲得對扇區的寫訪問權。然後,它會打開設備,通過偏移量遍歷收集的所有chunk,並使用WriteFile來填充先前準備好的隨機數據。
數據銷毀的代碼
下面的示例顯示了我們實驗中的一個日誌片段,當我們在惡意軟件執行期間轉儲特定結構體的內容時:首先收集數據,然後使用填充了隨機值的結構體來清除磁盤上的扇區:
小結如你所見,通過利用合法且無漏洞的簽名代碼,攻擊者就能夠繞過許多Windows安全機制。這會帶來非常嚴重的問題,因為出於安全考慮,用戶應用程序不應該在內核空間擁有這種程度的控制權限。
另外,我們想說明的是,這種情況下的數據恢復是非常困難的。攻擊者首先將文件碎片化,並將其散佈到磁盤各處,最後,用隨機數據覆蓋所有這些碎片。即使沒有最後一步(不分青紅皂白地向磁盤填充垃圾數據),只是進行碎片化並擦除所需結構體(如$MFT)的話,要想恢復如初也幾乎是不可能的。
需要注意的是,該惡意軟件為了隱藏自身的踪跡,還會設法破壞$LogFile和Windows事件等相關文件。