Skip to main content
Return to TrendAI™ 資安部落格
Cloud & supply chainCyber crime

Shai-hulud 2.0 蠕蟲攻擊鎖定雲端與開發環境

Shai-hulud 2.0 攻擊行動使用了一個精密的變種來竊取主要雲端平台及開發人員服務的登入憑證和機密,並透過自動化方式在受害者維護的 NPM 套件內植入後門。其進階手法使得它能在軟體供應鏈內部迅速擴散,造成無數的下游使用者陷入風險險。

MalwareCyber threatsCloudSoftware supply chainInformation technologyTechnology, media, & communicationsThreat reportsCloud security

Key Takeaways

  • Shai-hulud 2.0 延續了第一代變種的登入憑證竊取能力,從主要雲端平台竊取登入憑證與機密,還有 NPM 權杖 (token) 以及 GitHub 認證憑證,同時也新增了後門功能。
  • 此惡意程式可透過自動化方式入侵供應鏈,它會在受害者所維護的所有 NPM 套件內植入後門,然後再重新發布,使其在安裝時執行惡意檔案,製造出一種高傳染力的蠕蟲威脅,影響數千名下游使用者。
  • 惡意程式會利用偷來的雲端登入憑證來存取雲端原生機密管理服務,此外它還出現了破壞性行為,會在無法搜刮到所要的資料時,將使用者的資料清除。
  • Trend Vision One™ 已經可以偵測並攔截本文所列的入侵指標 (IoC),並為客戶提供量身訂做的威脅追蹤查詢、威脅洞見,以及威脅情報。

本文繼續討論我們對於 9 月 15 日發生的 Node Package Manager (NPM) 供應鏈攻擊所做的研究,駭客在該事件中發動了一起極針對性的網路釣魚攻擊來入侵某一名 NPM 封裝維護人員的帳號。我們在上一篇部落格中詳細說明了駭客在 JavaScript 套件內注入的惡意程式碼如何經由挾持網站 API 和操縱網路流量來移轉虛擬加密貨幣資產,以及攻擊中使用的 Shai-hulud 蠕蟲如何竊取雲端服務權杖、部署機密掃描工具,以及擴散至其他帳號。根據今年 11 月 24 日發生的一起事件通報指出,有數百個 NPM 儲存庫遭到了一波似乎是 Shai-hulud 的最新攻擊行動所入侵,因為其儲存庫的說明中寫著:「Shai-Hulud:The Second Coming。」(Shai-Hulud:二度降臨)"   

本文探討 Trend™ Research 針對 Shai-hulud 2.0 的研究發現,並揭露了一些在第一代變種所沒見過新功能。

根據我們對 Shai-hulud 2.0 的分析顯示,它會竊取 AWS、GCP 和 Azure 雲端廠商的登入憑證,包括 API 金鑰、權杖和密碼,以及 NPM 權杖和 GitHub 認證憑證。此外,惡意程式還會建立 GitHub Actions 工作流程來架設幕後操縱 (CC) 機制,同時也會注入專門用來竊取儲存庫機密的 GitHub Actions 工作流程機制。

除了竊取靜態的登入憑證之外,惡意程式還會利用偷來的雲端登入憑證來存取雲端原生機密管理服務:它會透過 AWS Secrets Manager API 來擷取 AWS 中的機密,透過 GCP Secret Manager API 來擷取 Google Cloud 中的機密,並透過 Azure Key Vault 來蒐集 Azure 中的機密。此外,惡意程式還會竊取 Azure Pod Identity 的登入憑證,這是一套至今仍廣泛使用的老舊系統,用來提供 Azure 身分給 Kubernetes Pod。 

除了這麼多功能之外,惡意程式還會自動在受害者所維護的每一個 NPM 套件內植入後門,然後再重新發布這些套件,使套件在安裝時執行惡意檔案,進而建立一個像蠕蟲般散播的管道,在 NPM 生態系內大量擴散並造成數千名信任這些套件的下游使用者遭到入侵。這整套工作流程都能完全自動化,而且一次最多可平行處理 100 個套件,不僅發揮最大的繁殖力,還能盡量降低被偵測的機率。

Shai-hulud 2.0 攻擊過程分析

Shai-Hulud 2.0 是以 NPM 套件的形式提供,內含惡意的預先安裝腳本,可在 NPM 安裝過程中自動執行 (modified package.json with "preinstall": "node setup_bun.jsv"這道指令)。 

Figure 1. Shai-hulud 2.0 high-Level workflow
Figure 1. Shai-hulud 2.0 high-Level workflow

setup_bun.js (載入器)

第一個植入器腳本 setup_bun.js 是一個會在 NPM 套件安裝過程中執行的載入器。其主要目的是要確保受害'系統上有 Bun JavaScript 執行時期環境可用,並且用它來執行主要的惡意程式檔案 (bun_environment.js)。

Figure 2. setup_bun.js execution flow
Figure 2. setup_bun.js execution flow

偵測及安裝 Bun 執行時期環境

安裝腳本會在初始階段偵測受害'系統上是否已安裝 Bun JavaScript 的執行時期環境。這個偵測程序會使用平台專用的指令來搜尋系統路徑 (PATH 環境變數) 是否含有 Bun 執行檔。在 Windows 系統上,該腳本會使用 where 指令,而在「類 Unix」系統上 (Linux 和 macOS) 則使用 which 指令。 

如果系統路徑上未偵測到 Bun,那麼腳本就會啟動自動下載與安裝程序。此程序會使用 Bun 官方網站 bun.sh 上提供的安裝腳本來讓安裝過程看起來正常,同時也降低被資安軟體偵測的可能性。 

在 Windows 上,該腳本會使用 PowerShell 來下載並執行安裝腳本 (透過 Invoke-RestMethodInvoke-Expression 兩個 cmdlet)。在類 Unix 系統上,則使用 curl 來下載安裝腳本,然後透過管線 (pipe) 直接將腳本傳送至 Bash 來執行。這樣的作法是仿照 Bun 官方的安裝指示,因此會讓惡意程式'的行為看起來很正常。

Figure 3. Checks if Bun is available on system PATH before attempting installation
Figure 3. Checks if Bun is available on system PATH before attempting installation

重新載入 PATH 環境變數

在安裝了 Bun 執行時期環境之後,安裝腳本必須重新載入系統的 PATH 環境變數,才能偵測到新安裝的執行檔。這是必要步驟,因為 Bun 安裝程序雖然會修改使用者'的 PATH 環境變數,但已經在執行安裝腳本的 Node.js 處理程序不會立即看到這些變更。PATH 環境變數重新載入機制會使用隨平台而異的技巧來擷取新的環境變數設定。

在 Windows 系統上,腳本會使用 PowerShell 來查詢 Windows 系統登錄以擷取使用者層級與電腦層級的 PATH 變數,然後將它們組合成單一的 PATH 字串。這麼做可確保 Bun 不論是安裝到使用者'目錄或系統目錄都能被偵測到。腳本會透過 PowerShell 來呼叫 [Environment]::GetEnvironmentVariable()這個 .NET 方法 (moethod) 來讀取這些登錄數值。

Figure 4. Powershell execution to query updated PATH from registry
Figure 4. Powershell execution to query updated PATH from registry

在類 Unix 系統上,PATH 的重新載入程序比較複雜,因為系統使用的指令列程式 (shell) 可能有很多種。腳本會試圖在使用者'的主目錄 (home) 中搜尋一般常見的指令列程式設定檔,包括:.bashrc、.bash_profile、.profile 以及 .zshrc

Figure 5. Powershell execution to query updated PATH from registry
Figure 5. Powershell execution to query updated PATH from registry

執行惡意檔案

安裝腳本在最後階段會執行實際的惡意程式檔案。 

首先,腳本會檢查系統路徑 (PATH) 當中是否含有 Bun,不論是之前曾經安裝,或者前面的步驟已成功安裝,此時就能找到。如果在 PATH 當中找不到,那麼腳本會在本機上搜尋一個原本可能隨附在惡意套件中的 Bun 執行檔。在搜尋本機 Bun 檔案時,它會檢查套件中也許隨附的一個 Bun-dist 目錄內是否有多個可能的檔案路徑,包括在各種子目錄結構中尋找 Unix 執行檔名稱 (bun) 和 Windows 執行檔名稱 (bun.exe)

Figure 6. Checking if Bun is available in the system PATH
Figure 6. Checking if Bun is available in the system PATH

一旦找到或安裝了 Bun 執行檔,接下來腳本就會執行惡意程式的主要檔案 bun_environment.js。腳本會產生 Bun 處理程序並指定一個惡意檔案作為參數。 

Figure 7. Execution of the main payload bun_environment.js
Figure 7. Execution of the main payload bun_environment.js

bun_environment.js (主要惡意檔案)

jy1() 函式

jy1() 函式是 Shai-hulud 2.0 惡意檔案的主要進入點。當「bun.exe bun_environment.js」這道指令執行時,這個函式會在腳本'的最上層被呼叫,然後負責協調整個攻擊過程。 

檢查 CI/CD 環境

惡意程式會檢查 CI/CD '環境變數來判斷自己是否正在一個持續整合流程當中執行,或是在開發人員'的本機電腦上執行。一旦偵測到,惡意程式就會立即執行而不是產生在背景,以便在建構流程當中取得登入憑證的最大存取權限。

Figure 8. Checking of CI/CD environments
Figure 8. Checking of CI/CD environments
  • BUILDKITE:Buildkite CI/CD 平台。
  • PROJECT_ID:Google Cloud Build。
  • GITHUB_ACTIONS:GitHub Actions 工作流程。
  • CODEBUILD_BUILD_NUMBER:AWS CodeBuild。
  • CIRCLE_SHA1:CircleCI 流程。

如果這些變數全部都不存在於 process.env 當中,那麼腳本就會認定它所在的環境是普通的開發人員電腦,而非 CI/CD 流程,這樣一來就觸發它偷偷地在背景處理程序當中執行。

在開發人員電腦上執行

當惡意程式在開發人員的電腦上執行時,它會採用一種隱匿的方式,利用 Bun.spawn().unref() 並搭配 POSTINSTALL_BG=1 環境變數旗標來產生一個分離的 (detached) 背景處理程序,好讓父處理程序能立即結束,這樣 NPM 安裝作業就能在正常時間內完成 (2-3 秒),以免使用者起疑。此時,背景的子處理程序會繼承所有的環境變數 (包括登入憑證) 並且持續執行來竊取登入憑證,讓使用者渾然不知。

Figure 9. Spawning of detached background process
Figure 9. Spawning of detached background process

aL0() 函式

aL0() 是一個非同步 JavaScript 函式,專門針對 CI/CD 環境和開發人員工作站而設計,用來竊取 AWS、GCP 和 Azure 雲端廠商的登入憑證,以及 NPM 權杖和 GitHub 認證憑證。當認證失敗時,惡意程式會執行一些破壞指令來清除使用者資料。此函式一開始會先偵測自己是否正在 GitHub Actions CI '環境內執行。

惡意程式使用了 3 個函式來建立常駐機制並停用 Linux 系統的安全控管。cQ0() 函式負責偵測處理程序,它會檢查惡意程式的代理程式是否已經在系統上執行,方法是搜尋處理程序清單中是否有「/home/agent/agent」。

Figure 10. Function used to check for /home/agent/agent in the process list
Figure 10. Function used to check for /home/agent/agent in the process list

pQ0() 函式會先測試是否有免密碼 (passwordless)sudo 存取權限可用,如果沒有,就會利用 Docker' 的特權容器存取權限來掛載主機檔案系統,然後修改 sudoers 組態檔案來授予自己無限制的系統管理員 (root) 權限。configuration file to grant itself unrestricted root privileges.

Figure 11. Function used for privilege escalation
Figure 11. Function used for privilege escalation

最後,gQ0() 函式負責停用資安控管,它會先停止 systemd-resolved DNS 服務,然後再使用一個惡意組態設定檔來將它重新設定。接著,有系統地清空 iptables 中的 OUTPUT 和 DOCKER-USER 兩條規則鏈 (chain) 裡面的防火牆規則,藉此清除可能阻擋(C &C) 通訊或資料外傳的任何網路過濾規則。

Figure 12. Function used to disable security controls
Figure 12. Function used to disable security controls

接下來,惡意程式會實例化 (instantiate) 針對三家主要雲端廠商和 GitHub 設計的模組,準備搜刮 AWS、Google Cloud Platform、Azure 和 GitHub 登入憑證。

Figure 13. Instantiation of modules for credential harvesting
Figure 13. Instantiation of modules for credential harvesting

擷取 NPM 權杖

惡意程式會使用一個登入憑證竊取模組來從開發人員的工作站擷取 NPM 認證權杖。它會搜尋兩個位置來尋找 .NPMrc 組態設定檔案:當前的工作目錄以及使用者'主目錄,也就是 NPM 用來儲存登錄認證憑證的標準位置。其函式會逐行讀取這些檔案,跳過註解和空白行,然後使用一個正規表達式 (regular expression) 來擷取符合 _authToken= or :_authToken= 格式且後面跟著一個 base64 編碼權杖字串的 NPM 認證權杖。

Figure 14. Function used to extract NPM authentication tokens
Figure 14. Function used to extract NPM authentication tokens

接著惡意程式會呼叫 NPM 的's /-/whoami API 端點來驗證權杖是否有效,然後取得已通過認證的使用者名稱。whoami 函式會檢驗 NPM 認證是否有效,並擷取受害者'的 NPM 使用者名稱,然後用這些資訊來查詢使用者所維護的套件。如果檢驗通過,就會儲存使用者名稱以便在後續階段用來列出受害者所維護的所有套件 (最多 100 個套件),接著再使用已通過檢驗的權杖將這些套件的後門化版本發布到 NPM 登錄。 

Figure 15. Validates victim's NPM token and retrieves username for package enumeration
Figure 15. Validates victim's NPM token and retrieves username for package enumeration

幕後操縱 (CC)

Figure 16. C&C Installation execution flow
Figure 16. C&C Installation execution flow

惡意程式會建立一個 GitHub 儲存庫並建立 CC 基礎架構。首先,它會確認 GitHub 認證是否可用,以確保惡意程式在繼續執行之前已取得有效的 GitHub 登入憑證。接著,它會產生一個 18 個字元的隨機識別碼來當成這個受感染受害者獨一無二的儲存庫名稱,確保每一台被入侵的電腦都有自己專屬的 CC 儲存庫。

透過 GitHub API 建立儲存庫

createRepo() 函式會使用 GitHub Octokit API 來呼叫 repos.createForAuthenticatedUser() 並使用隨機的名稱、以下說明文字「Shai-Hulud:Second Coming.」(Shai-Hulud:二度降臨)、以及特別的組態設定,包括可公開檢視、啟用討論功能,並且停用所有其他功能 (如:問題、專案和 wiki) 來讓儲存庫盡可能小巧並且只用來執行 CC 作業。with the random name, the description "Shai-Hulud: The Second Coming.", and specific configuration settings including public visibility, discussions enabled, and all other features like issues, projects, and wiki disabled to keep the repository minimal and focused on C&C operations.

Figure 17. Creation of attacker-controlled Git repository using the GitHub Octokit API
Figure 17. Creation of attacker-controlled Git repository using the GitHub Octokit API

成功建立儲存庫之後,它會從 API 的回應當中擷取儲存庫擁有者'的登入名稱和儲存庫名稱,然後以"owner/repo" 的格式建構出完整的儲存庫路徑,並儲存在 this.gitRepo 當中供日後參考。

Figure 18. Initialization and registration of the attacker-created GitHub repository
Figure 18. Initialization and registration of the attacker-created GitHub repository

檢驗工作流程權限

它會呼叫 checkWorkflowScope() 來檢驗偷來的 GitHub 權杖是否具備工作流程的 OAuth 範圍 (scope) 權限,這是管理 GitHub Actions 工作流程和執行器 (runner) 所需的權限。它會發送一個 HEAD 請求到 GitHub '的 API,然後檢查 x-oauth-scopes 回應標頭,裡面含有一個以逗號分隔的權杖權限清單。函式會解析這個標頭,檢查清單中是否存在「workflow」這個字,然後將結果快取到 this.hasWorkflow 當中。假使工作流程範圍確實存在,惡意程式就能繼續部署自行代管 (self-hosted) 的執行器和 CC 工作流程。

Figure 19. Workflow scope detection enabling deployment of malicious runners and C&C workflows
Figure 19. Workflow scope detection enabling deployment of malicious runners and C&C workflows

接著惡意程式會向 GitHub '的 API 取得執行器的註冊權杖,它會發送 POST 請求到 /repos/{owner}/{repo}/actions/runners/registration-token,對方會傳回一個臨時權杖,可用來向儲存庫註冊一個自行代管的執行器。

Figure 20. Requesting a GitHub runner registration token for self-hosted runner deployment
Figure 20. Requesting a GitHub runner registration token for self-hosted runner deployment

部署專為平台設計的執行器

根據偵測到的作業系統 (Linux、Windows 或 macOS) 而定,惡意程式會從官方 GitHub 版本下載對應的 GitHub Actions 執行器二進位檔案,然後解壓縮到一個隱藏目錄 (在 Unix 上為 $HOME/.dev-env ;在 Windows 上為使用者'主目錄),並使用註冊權杖來對它進行組態設定以便連回新建立的儲存庫 (執行器名稱為"SHA1HULUD")。該執行器會在背景啟動 (在 Unix 系統上使用 nohup;在 Windows 上則使用隱藏的 PowerShell 視窗),確保它在最初的惡意程式處理程序離開之後仍繼續執行,讓駭客能持續存取被入侵的電腦。

安裝 CC 工作流程

惡意程式會在儲存庫中建立一個 GitHub Actions 工作流程檔案 .github/workflows/discussion.yaml,該檔案會在有「討論」被建立時觸發,這樣一來駭客只要在儲存庫'的討論區張貼訊息,就能在受感染電腦上執行任意指令,這樣就完成了 C&C 基礎架構的架設。

Figure 21. Creation of a malicious GitHub Actions workflow enabling command execution via discussion posts
Figure 21. Creation of a malicious GitHub Actions workflow enabling command execution via discussion posts

反制分析的保險手段

如果 NPM 權杖無效或找不到,而且惡意程式也無法取得有效的 GitHub 權杖,它就會執行破壞指令來試圖刪除使用者'主目錄中的所有檔案。

Figure 22. Executed destructive commands if GitHub authentication fails and no NPM token is found
Figure 22. Executed destructive commands if GitHub authentication fails and no NPM token is found

蒐集系統資訊

接下來,惡意程式會蒐集詳細的系統資訊和 GitHub 登入憑證來建立受害系統的完整分析,包括:平台詳細資訊、主機名稱、作業系統使用者資訊,以及 GitHub 認證狀態。

Figure 23. Collection of system metadata and GitHub credentials for victim profiling
Figure 23. Collection of system metadata and GitHub credentials for victim profiling

建立惡意工作流程來將機密外傳

除了前述的 discussion.yaml 工作流程之外,惡意程式還會建立另一個專門用來竊取所有儲存庫機密的 GitHub Actions 工作流程。其工作流程注入機制會利用 GitHub '的原生 CI/CD 基礎架構,在程式碼被推送到已遭入侵的儲存庫時自動將敏感資料外傳。

Figure 24. Malicious workflow injection to retrieve GitHub secrets
Figure 24. Malicious workflow injection to retrieve GitHub secrets

工作流程注入機制

惡意程式會有系統地在被駭入的 GitHub 帳號可存取的每一個儲存庫中建立惡意的工作流程檔案。為了避免馬上被人發現,惡意程式會先建立一個含有時間戳記、且命名格式為add-linter-workflow-{timestamp}的臨時分支。這樣的分支命名方式看起來很正常,就跟一般程式開發人員會做的一樣,開發人員經常會在導入程式碼品質確保工具 (如 linter 和 formatter) 時會建立功能分支。藉由將惡意工作流程注入另一個分支而非直接注入主要分支,惡意程式就能降低觸發分支保護規則或即時程式碼審核程序而讓惡意活動曝光的風險。

Figure 25. Temporary Branch Creation for Workflow Injection
Figure 25. Temporary Branch Creation for Workflow Injection

惡意工作流程內容

惡意工作流程檔案在精心設計之下,看起來就像正常的程式碼格式美化工具,隱藏了它將登入憑證外傳的真實用途。其工作流程命名為「Code Formatter」,並設定成在每次推送 (push) 事件發生時觸發,確保它在開發人員將程式碼送交儲存庫時就會自動執行。這起攻擊的核心是利用 GitHub Actions' 內建的機密環境來存取儲存庫設定中的所有儲存庫機密。惡意程式利用 toJSON() 函式來將整個機密物件序列化成一個 JSON 字串,藉此擷取儲存庫內儲存的每一種登入憑證,包括:雲端供應商金鑰、API 權杖、資料庫密碼,以及簽署憑證。

Figure 26. Disguises secret exfiltration as code formatting workflow using toJSON(secrets)
Figure 26. Disguises secret exfiltration as code formatting workflow using toJSON(secrets)

監控工作流程的執行

在將惡意工作流程檔案注入目標儲存庫之後,惡意程式就會進入監控階段,在這階段,它會主動輪詢 GitHub Actions API 來偵測工作流程何時執行以及何時完成。惡意程式會建立一個逾時時間 30 秒的輪詢迴圈,每 2 秒重複查詢一次儲存庫'的工作流程,藉此偵測新建立工作流程的執行狀況。此一監控機制之所以必要,是因為工作流程只會在程式碼被推送到儲存庫時觸發,而且惡意程式必須等待某個現有的自動化流程或某個開發人員推送程式碼來觸發其惡意工作流程。 

Figure 27. Monitors GitHub Actions API with 30-second timeout to detect workflow execution and completion
Figure 27. Monitors GitHub Actions API with 30-second timeout to detect workflow execution and completion

下載完成品與擷取機密

一旦工作流程開始執行,惡意程式就會檢查工作流程的狀態來追蹤其進度,等待它轉換至「完成」狀態,然後再試著從工作流程完成品 (artifact) 中下載要外傳的機密。

Figure 28. Polls workflow run status to ensure execution completed before artifact download
Figure 28. Polls workflow run status to ensure execution completed before artifact download

工作流程一旦完成,惡意程式就會查詢 GitHub Actions Artifacts API 來列出工作流程執行時所產生的所有完成品。GitHub Actions 可讓工作流程上傳多個完成品,因此惡意程式必須列出完成品清單來找出含有已竊取機密的完成品。API 會傳回一個完成品物件的陣列,每個物件都包含完成品的 ID、名稱、大小以及下載網址等 metadata。惡意程式會逐一檢查這個陣列中的內容來尋找名為 "formatting" 的完成品,這是由惡意工作流程的 'actions/upload-artifact@v5 步驟所上傳。

Figure 29. Polls workflow run status to ensure execution completed before artifact download
Figure 29. Polls workflow run status to ensure execution completed before artifact download

GitHub 設計了一個兩步驟的完成品下載程序來提供安全、有時間限制的完成品儲存下載機制。惡意程式首先要請求取得完成品下載端點 (artifact download endpoint),提供完成品的 ID,並將重新導向參數設定為「manual」(手動),以避免自動重新導向。這第一個請求會傳回一個 Location (位置) 標頭,內含一個臨時、預先簽署的網址,指向實際的完成品儲存位置。惡意程式會從回應標頭中取出這個重導網址,接著再向這個臨時網址發送第二個 HTTP 請求來下載這個含有已竊取機密的 ZIP 檔案。

Figure 30. Retrieves redirect URL then downloads ZIP file from temporary storage location
Figure 30. Retrieves redirect URL then downloads ZIP file from temporary storage location

成功下載了完成品 ZIP 檔案之後,惡意程式接著必須解開含有已竊取儲存庫機密的 format.json 檔案。收到的下載回應是一個 ArrayBuffer,惡意程式會將它轉換成 Node.js Buffer 物件來進行後續處理。惡意程式會使用 ZIP 壓縮函式庫 (參照位置:tG0["default"]) 來開啟 ZIP 壓縮檔並呼叫 getEntry("format.json") 來找到含有已竊取機密的特定檔案。一旦找到,惡意程式就會解開檔案內的資料,然後將資料從二進位格式轉換成 UTF-8 字串,這是一個由惡意工作流程所蒐集到的 JSON 格式儲存庫機密。

搜刮多個雲端的登入憑證

惡意程式會從三大雲端廠商同步搜刮登入憑證,首先是取得所有的環境變數,包括 API 金鑰、權杖和密碼。接著,它會使用 AWS Secrets Manager API 來從 AWS 擷取機密,再透過 GCP Secret Manager API 來從 Google Cloud 擷取機密,最後透過 Azure Key Vault 來蒐集 Azure 的機密。 

Figure 31. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager APIs
Figure 31. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager APIs

蒐集 AWS 登入憑證

惡意程式會利用 AWS SDK' 內建的登入憑證供應者鏈 (credential provider chain) 來蒐集 AWS 登入憑證。這項機制會自動根據標準的 AWS 登入憑證查詢順序來搜尋環境變數中的登入憑證。其實作會透過一個擷取函式來檢查必要的 AWS 環境變數 (AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY)是否存在,然後伴隨著選擇性工作階段權杖一同擷取。 

Figure 32. Harvesting AWS credentials via the SDK’s environment variable lookup chain
Figure 32. Harvesting AWS credentials via the SDK’s environment variable lookup chain

登入憑證擷取函式會以非同步方式運作,內含詳細的記錄檔功能,這些功能可透過惡意程式'組態設定來啟用。當找到登入憑證時,函式會建立一個登入憑證物件,裡面包含存取金鑰 ID、機密存取金鑰,以及連線階段權杖 (若有的話)。此外,惡意程式還會擷取一些額外的登入憑證 metadata,包括:有效期限時間戳記、登入憑證範圍資訊,以及 AWS 帳號 ID。

Figure 33. Asynchronous AWS credential extraction with optional logging, returning keys, tokens, and metadata
Figure 33. Asynchronous AWS credential extraction with optional logging, returning keys, tokens, and metadata

此外,惡意程式還會利用 AWS CLI 規範所規定的檔案路徑解析功能來蒐集使用者'主目錄內儲存的 AWS 組態設定檔案。它會先檢查環境變數 (AWS_CONFIG_FILE 和 AWS_SHARED_CREDENTIALS_FILE) 當中指定的自訂檔案路徑,然後再檢查 ~/.aws/ 目錄中預設位置。

Figure 34. AWS config file harvesting via environment‑aware path resolution following standard AWS CLI conventions
Figure 34. AWS config file harvesting via environment‑aware path resolution following standard AWS CLI conventions

惡意程式內含專門用來竊取容器化環境登入憑證的特化功能,尤其是針對 AWS ECS (Elastic Container Service) 和 EKS (Elastic Kubernetes Service) 工作登入憑證機制。它會檢查是否有容器相關環境變數指向容器執行時期所暴露的登入憑證端點。這些端點提供了暫時的登入憑證,以及指派給容器工作的 IAM 角色權限,這些對於想要在雲端基礎架構內部橫向移動的駭客來說,非常珍貴。

Figure 35. Container‑specific credential harvesting from ECS/EKS task
Figure 35. Container‑specific credential harvesting from ECS/EKS task

蒐集 Google Cloud Platform (GCP) 登入憑證

此外,惡意程式也會攻擊 Google Cloud Platform' 的應用程式預設憑證 (ADC) 系統,這是 GCP 應用程式建議採用的認證方法。首先,它會檢查 GOOGLE_APPLICATION_CREDENTIALS 環境變數 (不區分大小寫),這通常會指向一個服務帳號的 JSON 金鑰檔案。當找到環境變數時,惡意程式會試圖讀取其指向的登入憑證檔案,然後擷取服務帳號的私密金鑰、用戶端電子郵件,以及專案資訊。

Figure 36. GCP ADC credential theft via GOOGLE_APPLICATION_CREDENTIALS and service account key extraction
Figure 36. GCP ADC credential theft via GOOGLE_APPLICATION_CREDENTIALS and service account key extraction

惡意程式會利用平台偵測邏輯來找到 Google Cloud SDK 的組態設定目錄,該目錄會儲存已認證使用者的登入憑證與專案設定。函式首先會檢查 CLOUDSDK_CONFIG 環境變數,此變數可讓使用者自訂組態設定目錄的位置。若此變數未設定,那麼惡意程式會根據作業系統來決定合適的預設路徑,例如在 Windows 系統上會使用 %APPDATA%\gcloud; Unix 系統上則使用 ~/.config/gcloud。 

一旦找到組態設定目錄,惡意程式就會存取該目錄內的多個登入憑證存放區,以及 gcloud CLI 所快取的各種認證權杖。

Figure 37. Platform‑aware discovery of the GCP SDK config directory and extraction of cached gcloud authentication tokens
Figure 37. Platform‑aware discovery of the GCP SDK config directory and extraction of cached gcloud authentication tokens

惡意程式特別針對 Application Default Credentials JSON 檔案設計了一個目標檔案存在檢查與擷取函式。實作上還採取了防禦式程式設計,它會檢查檔案是否存在 (existsSync) 來避免檔案不存在時可能出現的錯誤,此時它會默默將路徑設為空值 (null),而非產生可能觸發監控系統警報的例外情況 (exception)。

Figure 38. Targeted check for the ADC JSON file
Figure 38. Targeted check for the ADC JSON file

蒐集 Azure 登入憑證

惡意程式同樣設計了一套完整的搜刮系統來蒐集 Azure 的登入憑證,並且鎖定了 Azure 所有的驗證方法。這套系統首先會先定義一系列重要的 Azure 環境變數,然後有系統地檢查這些變數是否存在於處理程序所在的環境中。登入憑證供應者內含智慧解析邏輯,可處理複雜的組態設定,例如分號分隔的租戶清單與布林 boolean 旗標轉換。 

惡意程式會以詳細的格式記錄找到的環境變數,讓駭客掌握已駭入的系統上有哪些可用的認證方法。

Figure 39. Scanning of Azure-related environment variables
Figure 39. Scanning of Azure-related environment variables

惡意程式設計了一套連環的憑證擷取系統來嘗試各種 Azure 認證方法。這套方法首先會試圖利用用戶端機密來擷取 Service Principal (服務主體) 登入憑證 (自動化系統最常用的認證方法),這需要一個租戶 ID、用戶端 ID 和用戶端機密。如果這三項資料存在,那麼惡意程式就會實例化一個 ClientSecretCredential 物件來向 Azure Resource Manager 認證,並使用服務主體 (service principal) 所被授予的權限來存取 Azure 資源。每個成功擷取到的登入憑證都會跟著租戶 ID 和用戶端 ID 儲存到記錄檔內。

Figure 40. Azure credential harvesting to extract Service Principal credentials from available environment variables
Figure 40. Azure credential harvesting to extract Service Principal credentials from available environment variables

如果使用用戶端機密來認證時失敗,惡意程式就改回使用憑證來認證,並檢查是否有 AZURE_CLIENT_CERTIFICATE_PATHAZURE_CLIENT_CERTIFICATE_PASSWORD

Figure 41. Azure fallback to certificate‑based authentication by checking for client certificate paths and optional passwords.
Figure 41. Azure fallback to certificate‑based authentication by checking for client certificate paths and optional passwords.

接著,惡意程式會嘗試透過使用者名稱和/或密碼來認證,不過程式碼內含有警告訊息指出這方法已經過時。

Figure 42. Azure username/password authentication attempt
Figure 42. Azure username/password authentication attempt

此外,惡意程式還可擷取 Workload Identity 登入憑證,這是一套現代化 Azure 驗證機制,主要用於 Kubernetes 環境。惡意程式鎖定了三個關鍵的環境變數:AZURE_TENANT_ID、AZURE_CLIENT_ID 以及 AZURE_FEDERATED_TOKEN_FILE

Figure 43. Azure Workload Identity credential harvesting via key environment‑variable checks
Figure 43. Azure Workload Identity credential harvesting via key environment‑variable checks

此外,惡意程式還會竊取 Azure Pod Identity 的登入憑證,這是一套至今仍廣泛使用的老舊系統,用來提供 Azure 身分給 Kubernetes Pod。 

惡意程式會檢查 AZURE_POD_IDENTITY_AUTHORITY_HOST 環境變數是否存在,也就是提供受管理身分權杖的 Instance Metadata Service (IMDS) 端點。當這個變數存在時,惡意程式會將一個標準的金鑰路徑附加在授權主機後面來建構完整的 OAuth 權杖端點網址。 

若這個環境變數未設定,那麼惡意程式就會改回使用標準的 Azure IMDS 端點

Figure 44. Azure Pod Identity credential harvesting via IMDS endpoint discovery and token retrieval
Figure 44. Azure Pod Identity credential harvesting via IMDS endpoint discovery and token retrieval

透過雲端機密管理員

除了竊取靜態登入憑證之外,惡意程式還會使用偷來的雲端登入憑證存取雲端原生機密管理服務。 

程式碼中含有針對 AWS Secrets Manager、GCP Secret Manager 以及 Azure Key Vault 設計的流程,每一個都實作了 listAndRetrieveAllSecrets() 方法來列出並擷取這些服務中儲存的機密。

AWS Secrets Manager

AWS 機密搜刮模組採用一種跨區域機密搜刮策略以盡可能擷取到最多的機密。首先用 WX 類別 (class) 呼叫 STS GetCallerIdentity API 來驗證偷到的 AWS 登入憑證,以確保登入憑證有效,並擷取身分資訊 (使用者 ID、帳號 ID、ALN)。 

一旦登入憑證通過驗證,惡意程式就會逐一測試 AWS 的 17 個主要區域來尋找儲存在區域性 Secrets Manager 服務中的機密。針對每一區域,惡意程式會呼叫 ListSecrets API 來列出所有可用的機密,然後使用 GetSecretValue 來擷取每一項機密'的數值。

GCP Secret Manager

GCP 機密搜刮模組採用進階的認證處理方式,首先呼叫 Google Auth 函式庫中的 getAccessToken() 來檢驗其存取能力。一旦登入憑證確認可用之後,惡意程式就會呼叫 Resource Manager API 來列出所有可存取的 GCP 專案,然後為每個專案建立一個 Secret Manager 用戶端。它會使用 listSecrets() 來列出機密,並使用 {secretName}/versions/latest 路徑來擷取其最新版本,包括機密內容及相關的 metadata。

Figure 45. GCP secret harvesting via access‑token validation, project enumeration, and per‑project Secret Manager extraction
Figure 45. GCP secret harvesting via access‑token validation, project enumeration, and per‑project Secret Manager extraction

Azure Key Vault

Azure 機密搜刮流程瞄準了 Microsoft' 的 Key Vault 服務,它會先使用 DefaultAzureCredential 認證提供者來進行認證,它會依序嘗試多種認證方式 (環境變數、受管理的身分、Azure CLI 登入憑證)。一旦通過認證,惡意程式就會使用 Resource Manager API 來列出訂閱中的所有 Key Vault 資源,過濾出類型為 Microsoft.KeyVault/vaults 的資源。針對每一個找到的金庫 (vault),惡意程式都會實例化一個 SecretClient 然後逐一查看其所有機密,並擷取其數值。

Figure 46. Azure Key Vault harvesting via DefaultAzureCredential authentication, vault enumeration, and bulk secret extraction
Figure 46. Azure Key Vault harvesting via DefaultAzureCredential authentication, vault enumeration, and bulk secret extraction

使用 TruffleHog 來掃描機密

惡意程式使用了一套自動化 TruffleHog 部署系統,直接從 GitHub 下載、解壓縮以及快取最新版本的工具。在下載任何檔案之前,它會先檢查其快取內是否已經含有 TruffleHog 二進位檔案;如果沒有,就從 GitHub 擷取最新版本的資訊,然後針對各系統下載對應的二進位檔案,接著將檔案解壓縮之後,讓它變成可執行檔。

Figure 47. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager API
Figure 47. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager API

惡意程式會使用 GitHub API 來自動取得 TruffleHog 最新版本的資訊而不需經過認證,它會發送 GET 請求至 /repos/trufflesecurity/trufflehog/releases/latest 來取得 metadata 以及平台相關的下載連結。如此一來,就不需將版本編號寫死在程式內,同時也確保能與未來的更新相容。

Figure 48. Automated TruffleHog setup with release lookup
Figure 48. Automated TruffleHog setup with release lookup

惡意程式會使用 TruffleHog 的檔案系統掃描工具來掃描受害者的主目錄,尋找一些寫死的機密,呼叫該工具來協助解析機密內容並產生 JSON 輸出,也可使用選擇性參數來縮小或自訂掃描範圍。它會設定掃描時間為 10 分鐘,以避免長時間執行容易讓人起疑,並且同時擷取輸出和錯誤。掃描結果會封裝在一個結構化報告中,內含已發現的機密、錯誤、掃描持續時間,以及工具的結束碼,讓操作人員可以快速知道掃描的結果是成功、部分完成,或者失敗。

Figure 49. TruffleHog filesystem scan over the home directory, collecting JSON‑formatted findings, errors, duration, and exit codes
Figure 49. TruffleHog filesystem scan over the home directory, collecting JSON‑formatted findings, errors, duration, and exit codes

資料外傳

惡意程式建立了一套多重階段的資料外傳系統,將偷到的四種不同類別的資料彙整後上傳至惡意程式在受害者'的 GitHub 帳號底下自動建立 (且由駭客掌控) 的 Git 儲存庫。資料外傳過程會產生四個不同的 JSON 檔案,每個檔案都含有竊取到的特定資料類別。 

File Name Description
contents.json Aggregates system metadata such as platform details, hostname, OS user information, and GitHub authentication status.
environment.json Dumps all environment variables, capturing cloud credentials, API keys, database strings, and other sensitive config data.
cloud.json Contains secrets retrieved from AWS Secrets Manager, GCP Secret Manager, and Azure Key Vault, showing that the malware uses stolen credentials to access cloud-native secret stores.
truffleSecrets.json Stores TruffleHog scan results, including hardcoded secrets found in source code, config files, and other documents within the user's home directory.

經由供應鏈擴散

Figure 50. Supply Chain Compromise of NPM packages
Figure 50. Supply Chain Compromise of NPM packages

惡意程式會自動在受害者所維護的每一個 NPM 套件內植入後門,然後再重新發布套件,使它們在安裝期間執行惡意檔案。這樣就能建立一個像蠕蟲般散播的管道在 NPM 生態系內大量擴散,進而感染數千名信任該套件的下游使用者。 

其攻擊過程分成五個階段:搜尋受害者擁有的所有套件、下載原始的 tarball 壓縮檔、注入惡意的預先安裝掛勾 (preinstall hook)、綁入惡意程式安裝程式,最後將修改過的套件重新發布成正常的更新。 

這整套工作流程都能完全自動化,而且一次最多可平行處理 100 個套件,不僅發揮最大的繁殖力,還能盡量降低被偵測的機率。

取得維護人員的套件

惡意程式會向 NPM 登錄檔查詢來搜尋已通過認證的受害者所維護的所有套件。在實作中,它使用 NPM '的搜尋 API 搭配 maintainer: 過濾條件來列出受駭帳號所擁有的套件。列出套件時會包括每個套件的詳細 metadata,其中就包含了下載統計資料,這可讓惡意程式挑選要優先鎖定的高衝擊目標。駭客會依據每月下載數量來將套件排序,優先入侵最熱門的套件以盡可能擄獲最多受害者。

Figure 51. NPM Package Enumeration
Figure 51. NPM Package Enumeration

搜尋 NPM 登錄

在搜尋時,惡意程式會使用 NPM '的官方搜尋 API 端點 (/-/v1/search) 並搭配適當的網址編碼與認證標頭。這樣就可以突破 20 個套件的預設搜尋限制,實際攻擊時,惡意程式最多會請求到 100 個套件。

Figure 52. NPM Registry Search API Implementation
Figure 52. NPM Registry Search API Implementation

下載與解壓縮套件

惡意程式會從 NPM 登錄下載原始的 tarball 壓縮檔來準備修改。如此可確保其後門化版本仍維持原本的所有功能,然後再加入惡意功能。下載程序會使用標準的 HTTP 擷取與壓縮方式 (gzip、deflate、brotli) 有效率地下載套件。暫存目錄的命名方式 (NPM-update-) 會仿照正常 NPM 工具的作法來避免被處理程序監控系統偵測。

Figure 53. Package Tarball Download and Extraction
Figure 53. Package Tarball Download and Extraction

注入預先安裝掛勾

其供應鏈攻擊的核心是修改' package.json 檔案來注入惡意的預先安裝腳本。這個腳本會在任何人安裝此 NPM 套件'時自動執行 (甚至在該套件實際用到的相依元件安裝之前)。

Figure 54. Malicious package.json Modification
Figure 54. Malicious package.json Modification

預先安裝腳本會在 NPM 安裝過程中最早的階段執行,確切來說就是:

  • 在相依性解析之前。
  • 在套件安裝之前。
  • 在任何正常的套件程式碼執行之前。
  • 擁有使用者'的完整權限。

綁入惡意檔案

惡意程式會綁入一個精密的多重階段安裝腳本 (setup_bun.js) 來處理 Bun 執行時期環境的安裝以及惡意程式的執行,如本文一開始所提到。該腳本在設計上可同時支援 Windows、Linux 和 macOS 平台。

Figure 55. Injects setup_bun.js installer and bun_environment.js malware payload into package
Figure 55. Injects setup_bun.js installer and bun_environment.js malware payload into package

重新發布套件

惡意程式在注入惡意程式碼並綁入惡意檔案之後,就會重新包裝修改過的套件,接著使用受害者'的登入憑證將套件發布到 NPM。發布程序所使用的 NPM 認證權杖是在先前的攻擊中從受害者'取得的,這樣做可以讓這個惡意更新看起來像受信任的套件維護人員所發布。版本編號更新 (增量修補) 會依循版本編號的編排習慣,讓它看起來像是一次錯誤修正或小幅更新,這樣使用者在安裝時通常就不會仔細檢查。

Figure 56. Repackages modified package and publishes to NPM using victim's credentials
Figure 56. Repackages modified package and publishes to NPM using victim's credentials

採用 Trend Vision One™ 的主動式防護

Trend Vision One™ 是唯一將資安曝險管理與資安營運集中在一起的 AI 驅動企業網路資安平台,提供強大的多層式防護來保護企業內環境、混合環境,以及多重雲端環境。

以下是 Trend Vision One 提供的洞見、報告、上一篇部落格提到的查詢敘述,以及本文新增的資訊。 

Trend Vision One™ 威脅情報 

為了隨時掌握不斷演變的威脅,趨勢科技客戶可透過 Trend Vision One™ Threat Insights 來取得 Trend™ Research 有關新興威脅及駭客集團的最新洞見。

Trend Vision One Threat Insights  

Trend Vision One Intelligence Reports 應用程式 (IoC 掃描)

追蹤查詢

Trend Vision One Search 應用程式

Trend Vision One 客戶可以使用 Search 應用程式來尋找或追蹤本文提到的惡意指標,看看是否也出現在自己的環境中。

malName:*SHULUD* AND eventName: MALWARE_DETECTION AND LogType: detection

入侵指標 (IoC)

入侵指標請參閱此處