1. 概述

10 月中旬,部門老司機發給我一個 LSDMiner(舊稱 Watchdogsminer ) 最新活動中的一個樣本(MD5: 114d76b774185b826830cb6b015cb56f)。當時大概看了一眼,裏面用到了 DNS TXT 記錄來傳輸經過 AES 加密的數據,手頭忙別的事,就先擱下了。近來撿起來分析,Google 搜索樣本中用到的一個函數 NewAesCipher128() ,發現國外安全公司 Anomali 已經分析過這個 Case :

Anomali 的 Blog: Illicit Cryptomining Threat Actor Rocke Changes Tactics, Now More Difficult to Detect

以前的版本 一樣,LSDMiner 的樣本仍然是用 Go 編寫,但是內部代碼結構以及具體功能已經跟舊版本有很大差異。明顯的差異至少有以下 3 點:

  • 放棄了使用 Pastebin 作爲惡意 Shell 腳本的下發通道,轉而使用自己維護的 CC 服務器( *.systemten.org )來承載相關惡意活動;
  • 集成了多個漏洞 Exp,增強傳播能力,詳見 Anomali 的 Blog;
  • 利用 DNS TXT 記錄下發多種經過 AES 加密的數據,這些加密數據有以下幾種:
    • 最新的惡意 Cron 任務用到的惡意 Shell 腳本下載 URL,可以寫入失陷主機的 Cron 任務;
    • 最新的惡意樣本版本號,失陷主機上已有的惡意樣本會對比自己的版本號以決定是否 Update;
    • 最新的惡意 Shell 腳本;
    • 一系列最新二進制樣本的下載 URL。

其他惡意行爲按照常規的逆向分析方法按部就班分析即可,而關於加密的 DNS TXT 數據的逆向與解密過程,Anomali 的 Blog 中描述一帶而過,並沒詳述,按照他們 Blog 中簡單的描述,並不足以解密這些數據。本文就以上述樣本爲例,解析一下如何通過逆向樣本一步一步解密這些數據。

2. 惡意樣本執行流程

惡意樣本總體的執行流程分爲 3 步:

  1. 通過 DNS TXT 通道獲取用來篡改失陷主機 Cron 任務的惡意 URL,被篡改後的 Cron 任務會定期訪問惡意 URL 獲取最新的惡意 Shell 腳本;
  2. 掃描當前 B 段網絡,存活的 IP 嘗試利用 4 種方式入侵併植入,4 種方式有:
    • SSH 爆破;
    • Redis 未授權訪問;
    • Jenkins RCE 漏洞(CVE-2019-1003000)利用;
    • ActiveMQ RCE 漏洞(CVE-2016-3088)利用
  3. 持久駐留失陷主機、釋放礦機程序挖礦。

在最後第 3 步,也會通過 DNS TXT 通道獲取最新惡意 Shell 腳本以及二進制樣本的下載 URL。本文重點分析 DNS TXT 通道數據的獲取以及解密。

先看一下惡意樣本通過 DNS TXT 通道獲取最新的用來篡改失陷主機 Cron 任務的惡意 URL 的整體流程:

可以看到樣本首先從 cron.iap5u1rbety6vifaxsi9vovnc9jjay2l.com 獲取數據,然後用 AES-128bit 算法將其解密。再看一下從 cron.iap5u1rbety6vifaxsi9vovnc9jjay2l.com 獲取的加密數據:

DNS TXT 響應是一串字符,而且是經過 Base64 編碼的字符串 A7PZtADnYAEMEArGhmA9xQihPq9TRz4QigssjeOmUnQ 。函數 github_com_hippies_LSD_LSDC__AesCipher128_Decrypt() 中的處理流程可以證實這一點:

到這裏可以看出,要用 Go 語言編程解密這些數據,需要 3 步走:

  1. Base64 解碼 DNS TXT 的響應字串,得到待解密的二進制數據;
  2. 初始化 Go AES-128bit 解密句柄;
  3. 解密 Base64 解碼過的二進制數據。

3. Base64 解碼

先用 Linux 自帶的命令行工具 base64 嘗試解碼:

有點蹊蹺,不能用 base64 命令直接解碼,看來用的並不是標準的 Base64 編碼。這裏先補充一下關於 Base64 編碼的兩點背景知識:

  1. 參考: RFC4648 ,Base64 編碼主要有兩種: 標準編碼(StdEncoding)URL 安全的編碼(URLEncoding) 。標準 Base64 編碼的編碼字符表是 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ,而 URLEncoding 的編碼字符表則把 StdEncoding 編碼字符表中的 + 替換爲 ,把 / 替換爲 _ ,即 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
  2. Base64 兩種編碼的默認填充字符都是 = ,但也可以選擇不填充任何字符。

上述兩個知識點,在 Go 的 Base64 標準庫文檔 開頭就有說明:

兩個知識點各自分爲兩種情況,這樣組合起來就有 4 種細分的 Base64 Encoding:

那 LSDMiner 樣本中具體是用什麼樣的 Base64 解碼呢?需要先看一下樣本中 Base64 解碼的 Encoding 句柄是如何生成的。在函數 github_com_hippies_LSD_LSDC__AesCipher128_Decrypt() 中,是先拿到 Base64 解碼的 Encoding 句柄再進行解碼:

通過上面的 xrefs 信息,可知這個 b64EncodingObj 是在函數 encoding_base64_init() 中生成的。進入這個 init 函數, b64EncodingObj 生成過程如下:

可以看到這樣兩點:

  1. 調用 base64.NewEncoding() 函數時,傳入的參數是 URLEncoding 的編碼字符表,即樣本中用的是 URLEncoding 形式的 Base64 編碼;
  2. 調用 base64.URLEncoding.WithPadding() 函數時傳入的參數是 -1 ,即 base64.NoPadding ,不帶填充字符,即 base64.RawURLEncoding

至此就可以解碼 DNS TXT 響應的字符串了。測試代碼與結果如下:

4. AES 解密二進制數據

通過前面粗略的逆向分析,我們僅知道樣本中用了 AES-128bit 算法來解密數據,但這些知識遠不足以解密上面用 Base64 解碼得到的二進制數據。AES 加密算法此處不詳述,可以自行搜索相關資料,本文只關注如何用算法來解密數據。要想正確解密數據,還需要確定以下 AES 解密算法相關的幾個要素:

  • AES 密鑰;
  • AES 解密用到的 IV 向量;
  • AES 解密算法的分組密碼模式;
  • AES 解密算法的 Padding 方式。

上面的逆向分析過程中,我們注意到樣本中調用了函數 crypto_cipher_NewCBCDecrypter() ,可以確認樣本中用到的分組密碼模式是 CBC

在分析確認其他幾個要素之前,我們先捋一下兩個關鍵函數的邏輯:初始化 AES 解密句柄的 NewAesCipher128() 和 執行 AES 解密操作的 AesCipher128_Decrypt()

4.1 NewAesCipher128

首先,樣本調用該函數的時候傳入一個參數,即待查詢 DNS TXT 記錄的域名字符串 cron.iap5u1rbety6vifaxsi9vovnc9jjay2l.com

在函數內部先初始化一個 crypto/md5 句柄(代碼片段對照左邊標準庫函數 crypto_md5_New() 即可理解):

然後將傳入的域名字符串由 string 類型轉成字符切片並寫入 MD5 digest 對象,再通過 md5.digest.Sum() 函數做一次 MD5 Hash 計算(注意 Sum 函數傳入的參數爲 nil ):

再把這輪 MD5 計算的值通過 hex.EncodeToString() 轉成 32-bytes 的字符串,即常規的字符串形式的 MD5 值。然後取出再取出這個 MD5 值的 前 16 字節 ,保存到變量( r1HashStr_16bytes )中備用:

接下來,樣本又做了一次 MD5 計算,並且取出這一次 MD5 值的 後 16 字節 ,保存到變量中備用(注意,這一次 MD5 計算之前沒有調用 md5.dgest.Write() 來寫入新字節,並且調用 md5.digest.Sum() 函數時依然傳入參數 nil ):

後面可以看到,第一次 MD5 計算後取出的 前 16 字節 數據,被作爲 AES 密鑰 傳入 aes.NewCipher() 函數來初始化 AES 解密句柄:

而第二次 MD5 計算後取出的 後 16 字節 數據被保存起來,作爲本函數返回值的一部分返回,接下來作爲 AES 的 IV 向量 傳給後面函數 AesCipher128_Decrypt() 中調用的 crypto_cipher_NewCBCDecrypter() 函數。

4.2 AES 的 Padding 方式

前面內容分析確認了 AES 的 Key、IV 以及分組密碼模式,還需最後確認 AES 算法所用的 Padding 方式,即可正確解密數據。這一個點需要逆向分析函數 AesCipher128_Decrypt() 才能確認。

AES 加密算法用到的常見的 Padding 方式有以下幾種(參考: 對稱加密算法和分組密碼的模式 ):

  • ANSI X.923:也叫 ZeroPadding ,填充序列的最後一個字節填 paddingSize ,其它填0。
  • ISO 10126:填充序列的最後一個字節填 paddingSize , 其它填隨機數。
  • PKCS7:填充序列的每個字節都填 paddingSize

LSDMiner 中用到的 Padding 方式就是簡單的 ZeroPadding,通過函數 AesCipher128_Decrypt() 中解密操作後的 byte.Trim() 函數即可確認:

4.3 補充說明——關於二輪 MD5 值計算

上述分析過程中描述過,惡意樣本爲生成 AES 解密用到的 Key 和 IV 向量,對相應域名字符串連續做了 2 輪 MD5 Hash 計算,這一點 Anomali 的 Blog 中也提到了,只是他們沒提到 Key 和 IV 具體的生成過程。

然而樣本中連續兩輪的 MD5 計算的值其實是相同的——這是 Go 語言特有的 MD5 計算方式,參考 hash – Golang md5 Sum()函數 ,演示代碼如下:

這一點不知道是惡意軟件作者的失誤,還是有意爲之。倒是容易給逆向分析造成困擾,因爲乍一看“兩輪 MD5 計算”,很可能直觀認爲應該得出兩個不同的 MD5 值,並分別截取一段做 AES 解密的 Key 和 IV 向量,沒想到兩次 MD5 計算得出相同的值。

5. 完成解密

基於以上分析,就可以編寫程序完成我們想要的解密工作了。完整的 Go 語言代碼已上傳到 Github:

https://github.com/0xjiayu/LSDMiner_DNS_TXT_Decrypt

運行結果如下:

當前解密出來的 Cron URL 是 lsd.systemten.org ,在樣本中如果整個 DNS TXT 數據通道操作過程有任何異常而無法解密出最新的 Cron URL,備用的默認值也是這個 lsd.systemten.org :

6. 總結

文章開頭的截圖中已經顯示過,如果樣本用 net.LookupTXT() 函數檢索 DNS TXT 記錄失敗,還會跳轉到另外一個代碼分支,去用 DoH(DNS over HTTPS) 向 CloudFlare 的 DoH 服務器請求相應的 DNS TXT 記錄:

我們用命令行工具測試一下,可以看到這種方式也有效:

利用 DNS TXT 記錄和 DoH 下發惡意數據來輔助惡意樣本的運行,可以進一步提升整個 Botnet 基礎設施的健壯性和運營的靈活性,鑑於這個 Botnet 存活已久並不斷更新,應該引起業界的持續關注。

前文說過,惡意樣本中利用 DNS TXT 通道傳輸的數據還有其他幾種,方式都是一樣:檢索 DNS TXT 數據,用 base64.RawURLEncoding 解碼得到二進制數據;然後對域名進行 MD5 計算得出 AES 解密用到的 Key 和 IV,然後用 CBC 模式、ZeroPadding 的 AES-128bit 算法對 Base64 解碼後的二進制數據進行解密。對應的域名還有以下幾個,均可以用以上 Go 程序來解密:

"update.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"shell.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"1x32.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"2x32.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"3x32.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"1x64.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"2x64.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"
"3x64.iap5u1rbety6vifaxsi9vovnc9jjay2l.com"

另外,LSDMiner 涉及的二進制惡意樣本,都用變形 UPX 加了殼,而且殼的特徵很不明顯,難以用固定的特徵直接檢測加殼的樣本。並且,相關加殼二進制樣本的 UPX 殼幻數(Magic Number)還經常變化,比如本文分析的 MD5 爲 114d76b774185b826830cb6b015cb56f 的 UPX 殼幻數爲 0x2124922A ;最新的 x86_64 架構的樣本(MD5: 78e3582c42824f17aba17feefb87ea5f ) 的 UPX 殼幻數則變成了 0x215E77F2

相關文章