BAZARLOADER的主加載程序分析
我會在本文介紹這個加載程序的最后進程,它能夠下載和執行遠程有效負載,例如CobaltStrike和Conti勒索軟件。BazarLoader是基于Windows的惡意軟件,主要通過電子郵件等方式傳播。
第1步:檢查系統語言
與許多惡意軟件類似,BAZARLOADER會手動檢查系統的語言以避免在其母國開展惡意攻擊。
它調用GetSystemDefaultLangID來檢索系統的默認語言,并調用GetKeyboardLayoutList來遍歷系統的鍵盤布局。

對于每一種語言,惡意軟件都會使用位掩碼檢查其是否有效。
如果語言標識符大于0x43或小于0x18,則將其視為有效,這樣BAZARLOADER才會繼續執行。
如果它在0x18和0x43之間的范圍內,語言標識符和0x18之間的差異被用作位掩碼中要檢查的位的索引。
BAZARLOADER使用的位掩碼是0xD8080190C03,即二進制的11011000000010000000000110010000110000000011。如果語言ID為0x18,則檢查位掩碼中的第一位。如果語言ID為0x19,則檢查第二位,依此類推……

以下是惡意軟件避免的位掩碼中所有語言的列表。

第2步:互斥對象
為了檢查自身的多個運行實例,BAZARLOADER首先從其進程中提取SID的子權限。它通過調用GetTokenInformation來檢索進程的令牌完整性級別并調用GetSidSubAuthorityCount和GetSidSubAuthority來訪問SID的子權限來實現這一點。

如果SID的子權限是SECURITY_MANDATORY_SYSTEM_RID或SECURITY_MANDATORY_PROTECTED_PROCESS_RID,BAZARLOADER通過調用CreateMutexA檢查互斥鎖“{b837ef4f-10ee-4821-ac76-2331eb32a23f}”當前是否由任何其他進程擁有。
如果是,惡意軟件會自行終止。但是,檢查互斥對象是否存在的條件存在一個小錯誤,它假定它在實際成功時無法打開互斥對象。

此后,惡意軟件解析字符串“{0caa6ebb-cf78-4b01-9b0b-51032c9120ce}”并嘗試使用該名稱創建互斥鎖。

如果這個互斥對象已經存在,惡意軟件也會自行終止。
如果SID的子權限不是SECURITY_MANDATORY_SYSTEM_RID或SECURITY_MANDATORY_PROTECTED_PROCESS_RID,BAZARLOADER仍然使用這兩個互斥對象名稱,但在它們前面添加字符串“Global\”。這將檢查全局命名空間中的互斥鎖,而不是每個會話命名空間,這允許惡意軟件檢查它是否有在其他用戶的會話中運行的實例。
第3步:生成隨機Internet流量
為了生成Internet活動以隱藏其與C2服務程序的通信,BAZARLOADER首先調用InternetOpenA以使用以下字符串作為HTTP用戶代理來初始化WinINet函數的使用。

然后,惡意軟件會生成一個線程以定期連接到隨機URL,并利用以下結構生成噪音以隱藏主要的C2流量。

首先,BAZARLOADER調用InitializeCriticalSection來初始化結構的臨界區對象,該對象稍后用于保護對creation_flag字段的訪問。
接下來,它設置self字段指向結構,creation_flag字段為TRUE,并調用CreateThread生成一個線程來執行這些隨機Internet操作。如果創建線程失敗,creation_flag字段設置為FALSE。
線程首先嘗試獲取臨界區對象的所有權并檢查是否啟用了創建標志。如果是,惡意軟件會將以下URL解析為堆棧字符串。

接下來,線程進入一個無限循環,開始產生通訊噪音。對于隨機數生成,BAZARLOADER使用不同的函數調用Windows API BCryptGenRandom來生成一組隨機字節。
它隨機選擇上面列出的4個URL之一,隨機生成URL路徑段,然后將兩者結合起來構建完整的URL。

在生成路徑段時,該函數取生成的路徑段的最小和最大數量,以及每個路徑段的最小和最大長度。
它在給定范圍內隨機生成路徑段的計數。對于每個段,惡意軟件隨機生成一個字符串,該字符串在給定范圍內具有隨機長度,其中包含數字和大寫/小寫字母。

最后,惡意軟件調用InternetOpenURLA與生成的URL建立連接。它使用HTTP_QUERY_CONTENT_LENGTH標志調用HTTPQueryInfoA以檢索內容的長度,分配具有該大小的緩沖區,并調用InternetReadFile從該URL讀取數據。

這會重復進行,直到C2通信和有效負載注入完成,這會產生大量噪音來掩蓋進出C2服務程序的主要流量。
第4步:密碼結構集群
BAZARLOADER主要使用以下結構與C2服務程序進行通信。我們將在分析代碼時解釋結構的字段。

首先,它填充主結構中的crypto_struct字段。此結構包含稍后用于解密從C2服務程序發送的可執行文件的加密句柄。
結構可以重構如下:

這個惡意軟件會解析字符串“RSA”和“SHA384”,并調用BCryptOpenAlgorithmProvider來獲取這兩個算法的句柄。句柄存儲在crypto_struct結構中的相應字段中。

接下來,它解析內存中硬編碼的RSA公鑰和私鑰blob,以導入相應的密鑰句柄。

對于每個blob,惡意軟件會解析字符串“RSAFULLPRIVATEBLOB”或“RSAPUBLICBLOB”,并使用它指定blob的類型時調用BCryptImportKeyPair導入相應的密鑰句柄。

最后,它調用BCryptGetProperty來檢索RSA公鑰和私鑰密碼塊的長度。在完全填充了這個結構之后,BAZARLOADER現在可以執行RSA加密/解密以及SHA384哈希。
第5步:通過原始IP地址進行C2連接
在與C2服務程序通信之前,BAZARLOADER首先解析原始IP地址列表并將它們寫入主結構中的C2_addr_list字段。
該字段是一個表示字符串結構列表的結構,可以如下所示重新構建兩個字符串結構。

下面是此示例中使用的C2服務程序的所有IP地址的列表。

對于其中的每個地址,惡意軟件都會嘗試與相應的服務程序通信并下載下一階段的可執行文件。
要建立連接,它會填充以下結構。

惡意軟件調用InternetCrackUrlA來檢索C2的URL組件和InternetConnectA以連接到服務程序。

然后將此連接結構的字段復制到主結構的C2_connection_struct中。不過,我不完全確定他們為什么不直接填充主要結構。

同樣,BAZARLOADER填充下面的結構以創建對C2的HTTP請求。請求的對象名稱和HTTP動詞被解析為“/data/service”和“GET”。

請求的HTTP版本被解析為“HTTP/1.1”,并且BAZARLOADER調用HttpOpenRequestA來使用上面檢索到的連接句柄為C2服務器創建此請求。
它還調用InternetSetOptionA設置接收響應和發送請求的超時時間為300秒,連接C2的超時時間為120秒。

BAZARLOADER然后生成附加到請求的HTTP標頭。它通過調用GetSystemTime來使用當前日期和時間填充主結構的curr_system_time和datetime_string字段來實現這一點。
它還生成日期時間字符串的SHA384哈希,以填充結構的datetime_string_hash和datetime_string_hash_len字段。

接下來,BAZARLOADER通過調用BCryptSignHash使用其RSA私有簽名生成的哈希,并使用此哈希簽名隨機生成HTTP標頭。
下面是隨機HTTP標頭的形式。
BAZARLOADER的HTTP標頭

使用生成的HTTP標頭和請求句柄,BAZARLOADER調用HttpSendRequestA將請求發送到C2服務程序并調用HttpQueryInfoA來檢索狀態碼。
如果狀態碼不是HTTP_STATUS_OK,惡意軟件會轉移到另一個C2地址。

如果狀態碼為HTTP_STATUS_OK,BAZARLOADER調用InternetQueryDataAvailable確定要讀取的數據大小,根據大小分配內存緩沖區,并調用InternetReadFile讀取下一階段的有效載荷,直到所有內容都寫入內存。

最后,惡意軟件通過調用BCryptDecrypt使用其RSA公鑰解密有效負載,并檢查以確保有效負載的大小大于64字節并且包含MZ標頭。

第6步:通過自定義URL進行C2連接
如果BAZARLOADER無法從上面列出的IP地址下載下一階段的可執行文件,它會嘗試使用用戶擁有的DNS社區服務OpenNIC解析自定義C2域。
為了開始查詢OpenNIC的API,惡意軟件首先解析URL“api.opennicproject.org”并調用InternetConnectA建立與該網站的連接。

接下來,它調用HttpOpenRequestA以創建對象名稱為“/geoip/?bare&ipv=4&wl=all&res=8”的GET請求句柄,并使用HttpSendRequestA發送請求。
通過檢查OpenNIC的API,我們可以分解此對象名稱以查看BAZARLOADER請求的內容。其中“bare”參數只列出DNS服務器的IP地址,“IPv4”參數只列出IPv4服務器,“wl”參數只列出白名單服務器,“res”參數只列出8個服務器。
為了測試這一點,我們可以簡單地將下面的路徑粘貼到我們選擇的瀏覽程序中。

然后惡意軟件進入循環調用InternetQueryDataAvailable和InternetReadFile來讀取8個OpenNIC的DNS服務器到內存中。

對于每個DNS服務程序IP地址,BAZARLOADER將其從字符串解析為int并填充主結構中的opennic_server_struct字段。下面是用于存儲OpenNICIP地址的結構。

最后,惡意軟件解碼以下自定義C2域,嘗試使用DNS服務程序解析它們,并下載下一階段的可執行文件。

對于每個自定義域,BAZARLOADER調用DnsQuery_A從OpenNIC的服務程序查詢DNS資源記錄,以解析C2服務程序的IP地址。

在檢查IP地址是否有效后,惡意軟件會嘗試連接它并請求下載下一階段的可執行文件,類似于我們在上一步中看到的。

第7步:通過process hollowing技術注入
成功下載下一階段可執行文件之后,BAZARLOADER開始注入功能,從另一個進程啟動它。
對于此功能,BAZARLOADER填充以下結構。

首先,它檢查它的進程是否被提升為管理權限。它調用GetCurrentProcess和OpenProcessToken來檢索自己的進程令牌句柄,并調用GetTokenInformation來獲取令牌的提升信息。

如果進程沒有被提升,它會解析以下進程的名稱并嘗試填充注入結構的字段。

對于每個進程名稱,惡意軟件會枚舉進程的快照以檢索其ID,并調用OpenProcess以獲取其句柄。
為了填充包含進程命令行和完整路徑的full_exec_command和thread_curr_directory字段,BAZARLOADER首先從進程環境塊(PEB)中提取進程參數。
為了訪問PEB,惡意軟件調用NtQueryInformationProcess來檢索PEB的地址,并調用ReadProcessMemory將PEB讀入內存。

接下來,它調用ReadProcessMemory從進程的內存中讀取進程參數。

使用進程參數RTL_USER_PROCESS_PARAMETERS結構,BAZARLOADER讀取進程的命令行和完整路徑以填充注入結構。

同樣,它也使用process參數訪問瀏覽程序的環境塊并將其寫入注入結構。

如果BAZARLOADER具有管理員權限,而不是瀏覽程序的進程,它會嘗試從以下命令行使用svchost.exe進程填充注入結構。

接下來,利用注入結構,惡意軟件調用CreateProcessA來創建處于掛起狀態的目標進程以執行process hollowing。

我們可以通過被調用的WindowsAPI快速發現process hollowing正在發生。調用NtUnmapViewOfSection來取消映射并占用父級的內存。然后調用VirtualAllocEx和WriteProcessMemory在父進程中分配虛擬內存并將惡意負載寫入其中。

我們還可以看到,惡意軟件遍歷父節點的節頭以找到“.reloc”節,并對內存中注入的圖像執行重定位。

最后,BAZARLOADER調用SetThreadContext為父進程設置新的入口點,并調用ResumeThread再次恢復父進程,父進程將執行注入的可執行文件。

在此基礎上,我們分析了BAZARLOADER如何下載遠程可執行文件并使用進程空心化來執行它。