針對銀行木馬BokBot核心模組的深入分析
一、概述
BokBot惡意軟體由LUNAR SPIDER惡意組織開發和運營,在2017年首次出現,CrowdStrike的Falcon Overwatch和Falcon Intelligenc團隊對被感染主機進行了分析。最近,由於藉助MUMMY SPIDER惡意組織的Emotet惡意軟體進行分發活動,BokBot的感染數量又有所增加。
BokBot惡意軟體具有強大的功能,分為命令和控制、模組化兩類,具體功能包括:執行程序、登錄檔編輯、寫入檔案系統、登入、混淆、防篡改、竊取憑據、攔截代理、通過VNC進行遠端控制。
此外,已經發現BokBot可以從其他惡意軟體中下載並執行二進位制程式碼,例如Azorult infostealer。
本文將深入探討BokBot主要模組的技術細節。
二、BokBot容器執行
最初,BokBot被加密包裝。在最終解壓縮BokBot二進位制檔案並將其注入到svchost.exe之前,加密器經歷了幾個階段。以下是這些不同階段的簡單概述:
·第一階段(加密器):解碼第二階段的內容並執行。
· 第二階段(加密器):解碼ShellCode並執行。
· 第三階段(ShellCode):對基礎程序映像進行Hollow,解碼核心程序注入PE,使用核心程序注入PE覆蓋基本程序映像。
· 第四階段(程序注入):執行程序注入程式碼,啟動svchost.exe子程序,將BokBot作為無頭部的PE映像注入子程序。
我們使用CrowdStrike Falcon平臺,主要可以分析第四階段的過程。因此,本文的重點是BokBot注入子程序的這一過程,惡意軟體作者在這裡採用了一種獨特的方法。
2.1 程序注入
為了繞過反病毒檢測,從而可以對程序進行Hollowing,BokBot對幾個Windows API函式進行了掛鉤,並執行掛鉤程式碼,然後刪除鉤子。
2.1.1 模擬程序Hollowing過程
為了模擬這一過程,ZwCreateUserProcess例程將被首先掛鉤。BokBot呼叫ZwProtectVirtualMemory,將例程的許可權修改為PAGE_READWRITE。接下來,前五個操作碼(位元組)被替換為JMP<掛鉤程式碼地址>指令的操作碼。在恢復許可權後,呼叫CreateProcessA。
在呼叫CreateProcessA後,函式呼叫鏈將會走到呼叫ZwCreateUserProcess的地方,隨後呼叫掛鉤程式碼,如上圖所示。
掛鉤程式碼將通過從ZwCreateUserprocess例程中刪除掛鉤,來完成子程序的建立,然後呼叫沒有掛鉤的ZwCreateUserProcess過程。這樣一來,將會建立子程序,但在CreateProcessInternal返回之前不會執行。其餘的掛鉤例程將解碼,並將嵌入的BokBot二進位制檔案注入svchost.exe的子程序。
2.1.2 程式碼注入
在注入程式碼之前,BokBot PE首先被解壓縮,並被載入到本地程序記憶體中。載入後,以下Windows過程用於分配和寫入svchost子程序:
ZwAllocateVirtualMemory ZwWriteVirtualMemory ZwProtectVirtualMemory
將主BokBot模組寫入子程序後,將開始執行BokBot程式碼。
2.1.3 程式碼執行
BokBot採用了一種新穎的技術,來實現程式碼在子程序內部的執行。使用與之前相同的API,Dropper將掛鉤子程序中的RtlExitUserProcess。由於svchost.exe是在沒有引數的情況下啟動的,因此會立即終止。當程序嘗試退出時,它將呼叫掛鉤的RtlExitUserProcess,從而執行BokBot Payload。
在CreateProcessInternalW恢復執行之前,針對掛鉤的例程,還有一個任務要完成。
2.1.4 注入上下文資料結構
將BokBot Payload注入子程序後,要將上下文資料結構寫入子程序。下面的上下文中,包含所需的所有資料,可以確保BokBot的主模組在執行過程中不出現問題。
1、Windows過程地址:
ntdll.ZwAllocateVirtualMemory ntdll.ZwWriteVirtualMemory ntdll.ZwProtectVirtualMemory ntdll.ZwWaitForSingleObject ntdll.LdrLoadDll ntdll.LdrGetProcedureAddress ntdll.RtlExitUserProcess ntdll.ZwCreateUserProcess ntdll.RtlDecompressBuffer ntdll.ZwFlushInstructionCache
2、載入Payload的地址
3、Dropper二進位制檔案的路徑
4、C&C URL
5、專案ID
在Dropper程序的整個生命週期中,將會收集上述資料。此外,在下載和執行模組時,類似的結構將會被寫入到BokBot的子程序。
在注入後,CreatProcessInternalW會恢復,並且Dropper程序將退出。BokBot的主模組將進入到初始化階段。
三、BokBot初始化
在執行主迴圈和C&C通訊之前,BokBot首先經歷了幾個初始化步驟,為C&C通訊做好準備。初始化的具體步驟如下:
1、刪除RtlExitUserProcess的掛鉤
2、建立記憶體對映檔案,以儲存日誌記錄資料
3、以登入使用者身份,執行BokBot(如果當前程序是以System執行)
4、關閉錯誤告警
5、收集系統資訊:Windows版本資訊、使用者SID、域成員資訊
6、生成唯一ID
7、防止多次執行
8、在主機上安裝BokBot
9、將現有下載模組注入到子程序中
在下面,我們將詳細介紹其中的一些步驟。
3.1 關閉錯誤告警
為了防止出現錯誤提示,從而使受害者得知具體問題,BokBot將程序的錯誤模式設定為0x8007,具體對應如下:
SEM_FAILCRITICALERRORS SEM_NOALIGNMENTFAULTEXCEPT SEM_NOGPFAULTERRORBOX SEM_NOOPENFILEERRORBOX
這樣一來,將會禁用程序崩潰時產生的大多數錯誤通知。
3.2 生成唯一ID
在早期程序執行期間,生成了幾個唯一的ID,BokBot將使用這些ID。這些值將被傳遞給C&C伺服器,並會作為RC4加密演算法的金鑰,同時也傳遞給子程序。
3.2.1 專案ID
除了將主BokBot模組注入svchost之外,Dropper還會將一大塊二進位制資料注入,為BokBot執行提供上下文,其中也包括專案ID。這些唯一的專案ID值,似乎是用於識別與分發活動對應的感染。專案ID是一個4位元組值。
3.2.2 Bot ID
針對受感染主機上每個使用者的特定例項,其Bot ID是唯一的。該值將用作加密金鑰,以及生成BokBot各種唯一值所需的種子。例如,為檔案及事件名稱生成的偽隨機字串,我們將在後續章節中進一步討論。
Bot ID會以下面兩種方式中的一種來生成:
1、帳戶名稱的安全ID(Security ID)
2、檔案時間格式的系統時間
由於這兩個值都是64位,所以無論是用哪種方法,該值都會被分為兩個32位塊,並且會被異或。
3.2.3 ID雜湊值
除了Bot ID之外,還會生成一個簡單的雜湊值,用來驗證Bot ID和專案ID的有效性。該雜湊值使用Bot ID和專案ID來生成,具體方法如下:
hash = (( BotID / 0x100001E) & 0xFF) + ((BotID/0x10000) & 0xFF) hash = (ProjectID[3] + hash[0]) & 0xff hash = (BotID [0] + hash) & 0xff hash = (ProjectID[0] + hash[0]) & 0xff hash = (ProjectID[1] + hash[0]) & 0xff hash = (ProjectID[2] + hash[0]) & 0xff
該值將與專案ID、Bot ID一起作為C&C URL引數的一部分來傳遞。如果該請求無效,受感染的主機將不會收到來自C&C的任何指令。
四、C&C主機名初始化
Bokbot包含編碼後的C&C主機名列表,這些主機名會作為Dropper注入的上下文資料結構中的一部分。該結構中的C&C列表,也會使用由上下文提供的金鑰進行解碼,然後使用rdtsc指令生成的新金鑰進行重新編碼,最後儲存為指標陣列。
五、防止多次執行
使用Bot ID生成唯一的全域性命名事件。通過呼叫GetLastError,繼續成功呼叫CreateEvent。如果惡意軟體已經在執行,那麼最後一個產生的錯誤將會是ERROR_ALREADY_EXISTS,隨後程序將退出。
六、安裝過程
在安裝過程中,將BokBot Dropper二進位制檔案寫入安裝目錄,並建立計劃任務,以保證其永續性。
安裝目錄將在以下根目錄中建立:C:\ProgramData
安裝目錄的名稱是唯一的,使用Bot ID生成。在建立目錄後,使用Bot ID作為種子對原始的Dropper檔案進行重新命名,並寫入目錄。由於Bot ID基於系統資訊,因此將其用作種子就可以確保惡意軟體始終在特定主機上生成相同的安裝路徑和檔名。
在生成安裝目錄名稱後,BokBot需要為將要寫入該目錄的BokBot二進位制檔案生成一個檔名。下面的Python程式碼重現了BokBot用於生成檔名的演算法,以及其他各種字串。
指令碼中的str_id值是一個硬編碼的整數,與Bot ID一起使用,可以生成一致的字串。例如,使用Bot ID 0x2C6205B3以及str_id 2,總是會生成字串ayxhmenpqgof,但假如將str_id換為6,生成的字串就會變為bwjncm。
以下是安裝路徑的示例:
C:\ProgramData\{P6A23L1G-A21G-2389-90A1-95812L5X9AB8}\ruizlfjkex.exe
在這裡,將會建立計劃任務,以在Windows登入時執行。任務名稱的生成方式與安裝目錄相同。
· 任務名稱:{Q6B23L1U-A32L-2389-90A1-95812L5X9AB8}
· 觸發:登入時
· 動作:啟動一個程式
·詳細資訊:BokBot Dropper路徑
七、C&C通訊
BokBot通過HTTPS請求與C&C伺服器進行通訊,並通過URL引數和POST引數,將各種值傳遞給伺服器。除了伺服器使用的SSL/TLS之外,URL請求資料沒有進行加密或模糊處理。
下面詳細介紹了所有請求所必須的主要引數、一些其他的可選引數,以及Bot的註冊過程。
7.1 主要C&C請求和響應引數
每個請求或響應都會將這些引數傳送到伺服器。這些內容將會為C&C提供有關請求/響應型別以及唯一標識受感染計算機的資訊:
/in.php?g=2&c=3592L387P92A771N77&p=0&r=102
對於這些引數,更詳細的描述請參見下表:
URL路徑通常會在不同版本之間有所不同。例如,版本100-102使用的是/data100.php,而不是/in.php。
7.2 附加C&C請求和響應引數
在BokBot中,包含一個連續迴圈的通訊執行緒,該迴圈直至程序退出,持續從C&C伺服器檢索指令。除了上面已經描述的引數之外,請求中還包含一些附加引數。當Bot向C&C伺服器返回命令的執行結果時(例如:上傳螢幕截圖時),不會發送這些引數。
下面URL引數中,展示了與C&C初始連線的示例:
/in.php?g=2&c=42454B23<Bot ID>3B&p=0&r=104&i=0&n=0&o=0&k=3073&a=2&l=
在這個示例中,沒有Web注入,沒有C&C URL,也沒有下載任何模組,因此其中的一些重要引數值都為0或空。在這裡,生成了初始時間戳,版本號為靜態的。
八、初始Bot註冊
註冊請求會與每個傳送到C&C的標準C&C URL引數相組合。在初始請求之後,C&C伺服器將命令發回受害者主機,併發送訊號通知其下載Web注入、更新的C&C主機名、可執行模組或其他要執行的任務。
初始註冊URL中,包含與系統資訊相關的引數。以下字串是一個示例:
在下表中,描述了註冊URI引數:
下面是註冊請求(紅色)和C&C(藍色)響應的示例,其中包含傳送給受感染主機的命令。
8.1 C&C命令
在本節中,將分析C&C發出的命令請求。來自C&C的每個命令,都採用以下格式:
當前版本的BokBot中,提供了以下命令:
需要注意的是,這些命令的ID值可能會在不同版本之間更改。如此列表所示,BokBot為運營商提供了各種與受感染機器進行互動的選項。
8.2 URL下載命令處理程式
其中,許多命令都會觸發命令處理程式函式,該函式需要與C&C URL或伺服器請求引數中指定的其他URL進行通訊。如果請求指定,那麼從目標URL下載的資料就會被寫入到DAT。無論下載的資料是否被寫入到DAT檔案,它都將由以下C&C命令之一的回撥函式處理:
1、啟動一個新的可執行模組,重啟當前可執行模組。
2、更新Web注入(任一命令)
3、更新配置
4、更新BokBot
5、寫入檔案
6、下載並執行二進位制檔案
使用C&C URL主機名的命令,將會發送d URL引數,示例如下:
該值通常設定為0,要下載的檔案將由g引數指定。
九、模組和DAT檔案
從C&C接收的所有需要在重新啟動計算機後保留的資料,都會在受感染的計算機上寫入到DAT檔案中。這些檔案包括:Web注入配置、C&C配置和外部模組。
使用Bot ID作為金鑰,主模組和子模組將根據需要,對每個檔案進行加密和解密。每個模組都有一個唯一的標籤。
9.1 生成唯一標籤
BokBot會為注入的程序、下載的模組和下載的DAT檔案分配唯一的標籤。這些標籤是執行BokBot程序以識別外部程序資源的一種便捷方法。標籤的生成非常簡單:
· 18 – Web注入配置檔案,在二進位制檔案中靜態定義。
· 19 – 報告配置檔案,在二進位制檔案中靜態定義。
· 20 – C&C配置檔案,在二進位制檔案中靜態定義。
·33-46 – 下載的模組將注入子程序。需要以增量的方式分配,不一定是對模組所做的唯一標記。
在對BokBot的分析過程中,這些值將會定期出現,其中也包括生成唯一檔名的值,會在後面詳細描述。
十、下載DAT檔案
如前所述,DAT檔案是根據C&C傳送的命令進行下載。一旦從C&C接收到命令,就會呼叫特定於此命令的處理程式,來處理該請求。作為響應,受感染的機器將會通知C&C它已經準備好接收RC4加密後的Blob。下圖說明了下載配置檔案和模組的命令的過程。
一個8位元組的RC4命令將會被新增到資料緩衝區。在將Blob寫入檔案之前,BokBot會對檔案進行解密,然後使用基於Bot ID的新RC4金鑰,對其進行重新加密。
10.1 寫入檔案
BokBot在C:\ProgramData下建立一個新目錄來儲存DAT檔案。使用前面描述的字串生成演算法,生成目錄名稱。使用唯一標記值,生成DAT檔名。該唯一標記值也是通過字串生成演算法(取決於Bot ID)執行,該演算法會返回DAT檔案的唯一檔名。
上表中引用了我們測試過程中發現的所有DAT檔案。在我們的測試環境中,安裝目錄是C:\ProgramData\yyyyyyyyiu\。
10.2 可執行模組
BokBot有幾個可執行模組,可以下載並注入svchost.exe子程序。一旦使用RC4解碼相關的DAT檔案,就不需要對可執行模組DAT檔案進行額外的解碼或解壓縮。可執行模組頭包含識別模組所需的資訊:
檔案的其餘部分,包含載入和執行模組所需的資料,包括PE檔案的各個部分,以及自定義PE頭。
模組的注入與執行過程,使用類似於Dropper的技術來注入可執行模組,並且去掉了ZwCreateUserProcess的掛鉤,並暫停子程序啟動(CREATE_SUSPENDED)。通過新增RtlExitUserProcess掛鉤,它更接近於傳統的程序遷移。
1、PE影象載入
由於沒有標準的PE頭部,所以DAT檔案中必須包含所有相關資訊(虛擬大小、重定位等),以便將這個二進位制檔案正確對映到子程序。該資料是DAT檔案標頭的一部分。BokBot在將二進位制檔案注入子程序之前,現在本地程序記憶體中構建二進位制檔案。
2、注入
注入過程,使用與Dropper相同的API,也就是ZwAllocateVirtualMemory、ZwWriteVirtualMemory和ZwProtectVirtualMemory。在注入後,使用ResumeThread恢復程序。
3、執行上下文注入
再一次,在執行之前,先將執行上下文結構寫入子程序。這裡包含的一些資訊包括:Bot ID、專案ID、C&C主機名、URL路徑格式字串。
這樣一來,就可以保持父程序與子程序之間的一致性,不需要生成新的唯一識別符號,所有加密金鑰都是相同的:相同的主機名,甚至也有相同的URL路徑。父程序和子程序之間的一致性對於使用程序間通訊(IPC)在兩者之間傳送的訊息是非常必要的。
將模組注入子程序後,解密的DAT檔案的前四個位元組將新增到一個數組中,BokBot使用該陣列來標識當前正在執行的模組。
10.3 資料檔案
其他DAT檔案包含與C&C通訊或Web注入相關的所必須的資料。從本質上講,這些檔案提供了主要BokBot程序和可執行模組完成其工作所需的任何其他資料。
配置檔案包含BokBot主模組與C&C保持通訊所需的所有資料。一旦使用特定於程序的RC4金鑰解密檔案,就不再需要額外的解壓縮或者解密過程。
每個配置檔案都帶有一個數字簽名塊,用於驗證C&C主機名資料的完整性。根據混淆部分中說明的簽名驗證方法,來對簽名進行驗證。以下是C&C配置的示例,簽名塊為紅色:
其中,有多個Web注入檔案。一個包含所有目標URL和主機名資料,另一個包含正則表示式模式以及需要注入的程式碼。這些檔案都事先經過RC4加密和壓縮。
這些檔案並非由主BokBot二進位制檔案解析,而是由攔截代理模組對其進行解析。其過程是,驗證Zeus File Magic,分配緩衝區,然後解壓縮檔案。
十一、與子程序的通訊
BokBot使用記憶體對映檔案和事件,與包含注入模組的所有子程序進行通訊。通過使用CreateEvent、OpenEvent和OpenFileMapping利用命名事件的過程,BokBot主模組能夠為這些子程序提供附加資訊。
11.1 共享模組日誌
這些模組會寫入到同一個共享記憶體對映檔案中,並使用父程序和子程序之間的共享名稱建立記憶體對映檔案。可以生成此名稱的每個程序,都可以使用它來開啟記憶體對映檔案,並將資料寫入共享模組日誌。更多細節我們會在下一節中介紹,所編寫的具體資料會在下面單獨的模組描述中進行分析。主模組負責清除日誌,並將資料傳送到C&C伺服器。
11.2 特定模組的通訊
BokBot的主模組通常需要向包含注入模組程式碼的子程序發出命令。這些命令可以觸發模組中特定命令的更新,或者指示模組執行特定功能(例如:從Outlook收集資料)。下圖該輸了這一過程,我們也將在後續章節中進一步說明。
11.2.1 事件名稱生成
為了使BokBot主模組和子程序、事件之間成功通訊,需要生成唯一的名稱,並且必須在所有程序中保持一致。其具體方法如下:
父程序和子程序將使用這些事件來交換資料。
11.2.2 BokBot主模組
此過程能夠與注入模組的所有子項進行通訊。這些通訊都圍繞C&C發出的命令。一旦有需要通知可執行模組子程序的命令啟動,就會開啟一個命名的Q事件,以確保子程序已經準備好接收資料。如果Q事件不存在,那麼就表示尚未啟動子程序。BokBot將目標模組注入紫禁城中,並迴圈檢查,以檢視是否可以開啟該事件。
成功開啟Q事件後,BokBot將建立一個新的命名R事件,建立一個記憶體對映檔案(名稱為M event),將資料寫入檔案,併發出開啟Q事件的訊號,等待子程序的響應。子程序清除R事件後,記憶體對映檔案將被取消對映,所有控制代碼都將關閉。
11.2.3 BokBot可執行模組
初始化之後,子程序將建立一個命名的Q事件,並等待父程序發出訊號。在發出訊號後,將開啟命名的R事件,並處理記憶體對映檔案中的資料。
BokBot的主模組會將一些上下文資訊寫入注入的模組,並告訴它執行特定的操作。這些操作會根據接收資料的模組而有所更改。以下命令在模組之間是一致的,但執行的操作可能會有所不同:
·0xFF00 – 使用0x1122程式碼處理退出
· 0xFF01 – 檢查Web注入或無操作
· 0xFF02 – 更新C&C主機名
除了命令之外,還會基於命令,告知注入模組要完成的指令,以處理與命令相關聯的相關資料。
在父程序分配的任務完成後,將取消對映記憶體對映檔案,發出R事件訊號,並關閉所有其他開啟的事件。
十二、混淆與防篡改
BokBot使用幾種方法進行混淆以阻礙分析過程,分別是:字串混淆、從伺服器發出加密的DAT檔案、簽名驗證和多型性。
12.1 字串混淆
為了使分析更加困難,這裡使用了移位金鑰演算法對重要字串進行XOR編碼。所有編碼的字串都具有以下結構:
· 0x0 – DWORD – 初始XOR金鑰
· 0x4 – WORD – 字串大小
·0x8 – char[] – 編碼後字串
解碼字串的演算法如下(Python):
# # Encoded string structure # String_struct = “” xor_key = string_struct[:4] string_length = string_struct[4:6] ciphertext = string_strct[6:] plaintext = “” For idx in range(string_length) xor_key = (((xor_key >> 0x3) | (xor_key << 0x1D)) + idx) & 0xFFFFFFFF enc_byte = ciphertext[idx] if (enc_byte ^ (xor_key & 0xFF) != 0: plaintext += chr(enc_byte & (xor_key & 0xFF))
12.2 簽名驗證
簽名驗證會在以下兩種情況下發生:C&C URL發生更新、BokBot二進位制檔案發生更新。在這兩種情況下的驗證過程完全一致,驗證函式主要接收兩部分內容:要驗證的128位元組簽名,和要驗證的資料。
首先,BokBot建立需要驗證的資料的MD5雜湊值。接下來,嵌入在執行二進位制檔案中的RSA公鑰將通過CryptImportKey匯入。生成雜湊並匯入金鑰後,CryptVerifySignature將用於驗證簽名。這可能是為了防止某些第三方接管,或者以其他方式破壞殭屍網路。
12.3 多型性
每次安裝BokBot時,在將其寫入安裝目錄之前,都會使用垃圾資料修改二進位制檔案的.text部分,並更新其虛擬大小。同時,生成新的校驗和(Checksum),並替換當前的校驗和。
十三、總結
BokBot是一款功能強大的銀行木馬,可以為攻擊者提供強大的功能。BokBot最獨特之處之一是它與子模組通訊的方法。我們後續還將對該木馬進行持續分析,並將及時釋出最新成果。
十四、BokBot相關雜湊值
本文所涉及的全部樣本雜湊值如下:
十五、MITRE ATT&CK