重點摘要:
- 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 安裝過程中自動執行 (被修改過的 package.json 套件內含 "preinstall": "node setup_bun.js" 這道指令)。
setup_bun.js (載入器)
第一個植入器腳本 setup_bun.js 是一個會在 NPM 套件安裝過程中執行的載入器。其主要目的是要確保受害系統上有 Bun JavaScript 執行時期環境可用,並且用它來執行主要的惡意程式檔案 (bun_environment.js)。
偵測及安裝 Bun 執行時期環境
安裝腳本會在初始階段偵測受害系統上是否已安裝 Bun JavaScript 的執行時期環境。這個偵測程序會使用平台專用的指令來搜尋系統路徑 (PATH 環境變數) 是否含有 Bun 執行檔。在 Windows 系統上,該腳本會使用 where 指令,而在「類 Unix」系統上 (Linux 和 macOS) 則使用 which 指令。
如果系統路徑上未偵測到 Bun,那麼腳本就會啟動自動下載與安裝程序。此程序會使用 Bun 官方網站 (bun.sh) 上提供的安裝腳本來讓安裝過程看起來正常,同時也降低被資安軟體偵測的可能性。
在 Windows 上,該腳本會使用 PowerShell 來下載並執行安裝腳本 (透過 Invoke-RestMethod 和 Invoke-Expression 兩個 cmdlet)。在類 Unix 系統上,則使用 curl 來下載安裝腳本,然後透過管線 (pipe) 直接將腳本傳送至 Bash 來執行。這樣的作法是仿照 Bun 官方的安裝指示,因此會讓惡意程式的行為看起來很正常。
重新載入 PATH 環境變數
在安裝了 Bun 執行時期環境之後,安裝腳本必須重新載入系統的 PATH 環境變數,才能偵測到新安裝的執行檔。這是必要步驟,因為 Bun 安裝程序雖然會修改使用者的 PATH 環境變數,但已經在執行安裝腳本的 Node.js 處理程序不會立即看到這些變更。PATH 環境變數重新載入機制會使用隨平台而異的技巧來擷取新的環境變數設定。
在 Windows 系統上,腳本會使用 PowerShell 來查詢 Windows 系統登錄以擷取使用者層級與電腦層級的 PATH 變數,然後將它們組合成單一的 PATH 字串。這麼做可確保 Bun 不論是安裝到使用者目錄或系統目錄都能被偵測到。腳本會透過 PowerShell 來呼叫 [Environment]::GetEnvironmentVariable() 這個 .NET 方法 (moethod) 來讀取這些登錄數值。
在類 Unix 系統上,PATH 的重新載入程序比較複雜,因為系統使用的指令列程式 (shell) 可能有很多種。腳本會試圖在使用者的主目錄 (home) 中搜尋一般常見的指令列程式設定檔,包括:.bashrc、.bash_profile、.profile 以及 .zshrc。
執行惡意檔案
安裝腳本在最後階段會執行實際的惡意程式檔案。
首先,腳本會檢查系統路徑 (PATH) 當中是否含有 Bun,不論是之前曾經安裝,或者前面的步驟已成功安裝,此時就能找到。如果在 PATH 當中找不到,那麼腳本會在本機上搜尋一個原本可能隨附在惡意套件中的 Bun 執行檔。在搜尋本機 Bun 檔案時,它會檢查套件中也許隨附的一個 Bun-dist 目錄內是否有多個可能的檔案路徑,包括在各種子目錄結構中尋找 Unix 執行檔名稱 (bun) 和 Windows 執行檔名稱 (bun.exe)。
一旦找到或安裝了 Bun 執行檔,接下來腳本就會執行惡意程式的主要檔案 bun_environment.js。腳本會產生 Bun 處理程序並指定一個惡意檔案作為參數。
bun_environment.js (主要惡意檔案)
jy1() 函式
jy1() 函式是 Shai-hulud 2.0 惡意檔案的主要進入點。當「bun.exe bun_environment.js」這道指令執行時,這個函式會在腳本的最上層被呼叫,然後負責協調整個攻擊過程。
檢查 CI/CD 環境
惡意程式會檢查 CI/CD 環境變數來判斷自己是否正在一個持續整合流程當中執行,或是在開發人員的本機電腦上執行。一旦偵測到,惡意程式就會立即執行而不是產生在背景,以便在建構流程當中取得登入憑證的最大存取權限。
- 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 秒),以免使用者起疑。此時,背景的子處理程序會繼承所有的環境變數 (包括登入憑證) 並且持續執行來竊取登入憑證,讓使用者渾然不知。
aL0() 函式
aL0() 是一個非同步 JavaScript 函式,專門針對 CI/CD 環境和開發人員工作站而設計,用來竊取 AWS、GCP 和 Azure 雲端廠商的登入憑證,以及 NPM 權杖和 GitHub 認證憑證。當認證失敗時,惡意程式會執行一些破壞指令來清除使用者資料。此函式一開始會先偵測自己是否正在 GitHub Actions CI 環境內執行。
惡意程式使用了 3 個函式來建立常駐機制並停用 Linux 系統的安全控管。cQ0() 函式負責偵測處理程序,它會檢查惡意程式的代理程式是否已經在系統上執行,方法是搜尋處理程序清單中是否有「/home/agent/agent」。
pQ0() 函式會先測試是否有免密碼 (passwordless) 的 sudo 存取權限可用,如果沒有,就會利用 Docker 的特權容器存取權限來掛載主機檔案系統,然後修改 sudoers 組態檔案來授予自己無限制的系統管理員 (root) 權限。
最後,gQ0() 函式負責停用資安控管,它會先停止 systemd-resolved DNS 服務,然後再使用一個惡意組態設定檔來將它重新設定。接著,有系統地清空 iptables 中的 OUTPUT 和 DOCKER-USER 兩條規則鏈 (chain) 裡面的防火牆規則,藉此清除可能阻擋 CC 通訊或資料外傳的任何網路過濾規則。
接下來,惡意程式會實例化 (instantiate) 針對三家主要雲端廠商和 GitHub 設計的模組,準備搜刮 AWS、Google Cloud Platform、Azure 和 GitHub 登入憑證。
擷取 NPM 權杖
惡意程式會使用一個登入憑證竊取模組來從開發人員的工作站擷取 NPM 認證權杖。它會搜尋兩個位置來尋找 .NPMrc 組態設定檔案:當前的工作目錄以及使用者主目錄,也就是 NPM 用來儲存登錄認證憑證的標準位置。其函式會逐行讀取這些檔案,跳過註解和空白行,然後使用一個正規表達式 (regular expression) 來擷取符合 _authToken= or :_authToken= 格式且後面跟著一個 base64 編碼權杖字串的 NPM 認證權杖。
接著惡意程式會呼叫 NPM 的 /-/whoami API 端點來驗證權杖是否有效,然後取得已通過認證的使用者名稱。whoami 函式會檢驗 NPM 認證是否有效,並擷取受害者的 NPM 使用者名稱,然後用這些資訊來查詢使用者所維護的套件。如果檢驗通過,就會儲存使用者名稱以便在後續階段用來列出受害者所維護的所有套件 (最多 100 個套件),接著再使用已通過檢驗的權杖將這些套件的後門化版本發布到 NPM 登錄。
幕後操縱 (CC)
惡意程式會建立一個 GitHub 儲存庫並建立 CC 基礎架構。首先,它會確認 GitHub 認證是否可用,以確保惡意程式在繼續執行之前已取得有效的 GitHub 登入憑證。接著,它會產生一個 18 個字元的隨機識別碼來當成這個受感染受害者獨一無二的儲存庫名稱,確保每一台被入侵的電腦都有自己專屬的 CC 儲存庫。
- 透過 GitHub API 建立儲存庫
createRepo() 函式會使用 GitHub Octokit API 來呼叫 repos.createForAuthenticatedUser() 並使用隨機的名稱、以下說明文字「Shai-Hulud:Second Coming.」(Shai-Hulud:二度降臨)、以及特別的組態設定,包括可公開檢視、啟用討論功能,並且停用所有其他功能 (如:問題、專案和 wiki) 來讓儲存庫盡可能小巧並且只用來執行 CC 作業。
成功建立儲存庫之後,它會從 API 的回應當中擷取儲存庫擁有者的登入名稱和儲存庫名稱,然後以「owner/repo」的格式建構出完整的儲存庫路徑,並儲存在 this.gitRepo 當中供日後參考。
- 檢驗工作流程權限
它會呼叫 checkWorkflowScope() 來檢驗偷來的 GitHub 權杖是否具備工作流程的 OAuth 範圍 (scope) 權限,這是管理 GitHub Actions 工作流程和執行器 (runner) 所需的權限。它會發送一個 HEAD 請求到 GitHub 的 API,然後檢查 x-oauth-scopes 回應標頭,裡面含有一個以逗號分隔的權杖權限清單。函式會解析這個標頭,檢查清單中是否存在「workflow」這個字,然後將結果快取到 this.hasWorkflow 當中。假使工作流程範圍確實存在,惡意程式就能繼續部署自行代管 (self-hosted) 的執行器和 CC 工作流程。
接著惡意程式會向 GitHub 的 API 取得執行器的註冊權杖,它會發送 POST 請求到 /repos/{owner}/{repo}/actions/runners/registration-token,對方會傳回一個臨時權杖,可用來向儲存庫註冊一個自行代管的執行器。
- 部署專為平台設計的執行器
根據偵測到的作業系統 (Linux、Windows 或 macOS) 而定,惡意程式會從官方 GitHub 版本下載對應的 GitHub Actions 執行器二進位檔案,然後解壓縮到一個隱藏目錄 (在 Unix 上為 $HOME/.dev-env;在 Windows 上為使用者主目錄),並使用註冊權杖來對它進行組態設定以便連回新建立的儲存庫 (執行器名稱為「SHA1HULUD」)。該執行器會在背景啟動 (在 Unix 系統上使用 nohup;在 Windows 上則使用隱藏的 PowerShell 視窗),確保它在最初的惡意程式處理程序離開之後仍繼續執行,讓駭客能持續存取被入侵的電腦。
- 安裝 CC 工作流程
惡意程式會在儲存庫中建立一個 GitHub Actions 工作流程檔案 .github/workflows/discussion.yaml,該檔案會在有「討論」被建立時觸發,這樣一來駭客只要在儲存庫的討論區張貼訊息,就能在受感染電腦上執行任意指令,這樣就完成了 CC 基礎架構的架設。
- 反制分析的保險手段
如果 NPM 權杖無效或找不到,而且惡意程式也無法取得有效的 GitHub 權杖,它就會執行破壞指令來試圖刪除使用者主目錄中的所有檔案。
- 蒐集系統資訊
接下來,惡意程式會蒐集詳細的系統資訊和 GitHub 登入憑證來建立受害系統的完整分析,包括:平台詳細資訊、主機名稱、作業系統使用者資訊,以及 GitHub 認證狀態。
建立惡意工作流程來將機密外傳
除了前述的 discussion.yaml 工作流程之外,惡意程式還會建立另一個專門用來竊取所有儲存庫機密的 GitHub Actions 工作流程。其工作流程注入機制會利用 GitHub 的原生 CI/CD 基礎架構,在程式碼被推送到已遭入侵的儲存庫時自動將敏感資料外傳。
工作流程注入機制
惡意程式會有系統地在被駭入的 GitHub 帳號可存取的每一個儲存庫中建立惡意的工作流程檔案。為了避免馬上被人發現,惡意程式會先建立一個含有時間戳記、且命名格式為 add-linter-workflow-{timestamp} 的臨時分支。這樣的分支命名方式看起來很正常,就跟一般程式開發人員會做的一樣,開發人員經常會在導入程式碼品質確保工具 (如 linter 和 formatter) 時會建立功能分支。藉由將惡意工作流程注入另一個分支而非直接注入主要分支,惡意程式就能降低觸發分支保護規則或即時程式碼審核程序而讓惡意活動曝光的風險。
惡意工作流程內容
惡意工作流程檔案在精心設計之下,看起來就像正常的程式碼格式美化工具,隱藏了它將登入憑證外傳的真實用途。其工作流程命名為「Code Formatter」,並設定成在每次推送 (push) 事件發生時觸發,確保它在開發人員將程式碼送交儲存庫時就會自動執行。這起攻擊的核心是利用 GitHub Actions 內建的機密環境來存取儲存庫設定中的所有儲存庫機密。惡意程式利用 toJSON() 函式來將整個機密物件序列化成一個 JSON 字串,藉此擷取儲存庫內儲存的每一種登入憑證,包括:雲端供應商金鑰、API 權杖、資料庫密碼,以及簽署憑證。
監控工作流程的執行
在將惡意工作流程檔案注入目標儲存庫之後,惡意程式就會進入監控階段,在這階段,它會主動輪詢 GitHub Actions API 來偵測工作流程何時執行以及何時完成。惡意程式會建立一個逾時時間 30 秒的輪詢迴圈,每 2 秒重複查詢一次儲存庫的工作流程,藉此偵測新建立工作流程的執行狀況。此一監控機制之所以必要,是因為工作流程只會在程式碼被推送到儲存庫時觸發,而且惡意程式必須等待某個現有的自動化流程或某個開發人員推送程式碼來觸發其惡意工作流程。
下載完成品與擷取機密
一旦工作流程開始執行,惡意程式就會檢查工作流程的狀態來追蹤其進度,等待它轉換至「完成」狀態,然後再試著從工作流程完成品 (artifact) 中下載要外傳的機密。
工作流程一旦完成,惡意程式就會查詢 GitHub Actions Artifacts API 來列出工作流程執行時所產生的所有完成品。GitHub Actions 可讓工作流程上傳多個完成品,因此惡意程式必須列出完成品清單來找出含有已竊取機密的完成品。API 會傳回一個完成品物件的陣列,每個物件都包含完成品的 ID、名稱、大小以及下載網址等 metadata。惡意程式會逐一檢查這個陣列中的內容來尋找名為「formatting」的完成品,這是由惡意工作流程的 actions/upload-artifact@v5 步驟所上傳。
GitHub 設計了一個兩步驟的完成品下載程序來提供安全、有時間限制的完成品儲存下載機制。惡意程式首先要請求取得完成品下載端點 (artifact download endpoint),提供完成品的 ID,並將重新導向參數設定為「manual」(手動),以避免自動重新導向。這第一個請求會傳回一個 Location (位置) 標頭,內含一個臨時、預先簽署的網址,指向實際的完成品儲存位置。惡意程式會從回應標頭中取出這個重導網址,接著再向這個臨時網址發送第二個 HTTP 請求來下載這個含有已竊取機密的 ZIP 檔案。
成功下載了完成品 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 的機密。
蒐集 AWS 登入憑證
惡意程式會利用 AWS SDK 內建的登入憑證供應者鏈 (credential provider chain) 來蒐集 AWS 登入憑證。這項機制會自動根據標準的 AWS 登入憑證查詢順序來搜尋環境變數中的登入憑證。其實作會透過一個擷取函式來檢查必要的 AWS 環境變數 (AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY) 是否存在,然後伴隨著選擇性工作階段權杖一同擷取。
登入憑證擷取函式會以非同步方式運作,內含詳細的記錄檔功能,這些功能可透過惡意程式組態設定來啟用。當找到登入憑證時,函式會建立一個登入憑證物件,裡面包含存取金鑰 ID、機密存取金鑰,以及連線階段權杖 (若有的話)。此外,惡意程式還會擷取一些額外的登入憑證 metadata,包括:有效期限時間戳記、登入憑證範圍資訊,以及 AWS 帳號 ID。
此外,惡意程式還會利用 AWS CLI 規範所規定的檔案路徑解析功能來蒐集使用者主目錄內儲存的 AWS 組態設定檔案。它會先檢查環境變數 (AWS_CONFIG_FILE 和 AWS_SHARED_CREDENTIALS_FILE) 當中指定的自訂檔案路徑,然後再檢查 ~/.aws/ 目錄中預設位置。
惡意程式內含專門用來竊取容器化環境登入憑證的特化功能,尤其是針對 AWS ECS (Elastic Container Service) 和 EKS (Elastic Kubernetes Service) 工作登入憑證機制。它會檢查是否有容器相關環境變數指向容器執行時期所暴露的登入憑證端點。這些端點提供了暫時的登入憑證,以及指派給容器工作的 IAM 角色權限,這些對於想要在雲端基礎架構內部橫向移動的駭客來說,非常珍貴。
蒐集 Google Cloud Platform (GCP) 登入憑證
此外,惡意程式也會攻擊 Google Cloud Platform 的應用程式預設憑證 (ADC) 系統,這是 GCP 應用程式建議採用的認證方法。首先,它會檢查 GOOGLE_APPLICATION_CREDENTIALS 環境變數 (不區分大小寫),這通常會指向一個服務帳號的 JSON 金鑰檔案。當找到環境變數時,惡意程式會試圖讀取其指向的登入憑證檔案,然後擷取服務帳號的私密金鑰、用戶端電子郵件,以及專案資訊。
惡意程式會利用平台偵測邏輯來找到 Google Cloud SDK 的組態設定目錄,該目錄會儲存已認證使用者的登入憑證與專案設定。函式首先會檢查 CLOUDSDK_CONFIG 環境變數,此變數可讓使用者自訂組態設定目錄的位置。若此變數未設定,那麼惡意程式會根據作業系統來決定合適的預設路徑,例如在 Windows 系統上會使用 %APPDATA%\gcloud,在類 Unix 系統上則使用 ~/.config/gcloud。
一旦找到組態設定目錄,惡意程式就會存取該目錄內的多個登入憑證存放區,以及 gcloud CLI 所快取的各種認證權杖。
惡意程式特別針對 Application Default Credentials JSON 檔案設計了一個目標檔案存在檢查與擷取函式。實作上還採取了防禦式程式設計,它會檢查檔案是否存在 (existsSync) 來避免檔案不存在時可能出現的錯誤,此時它會默默將路徑設為空值 (null),而非產生可能觸發監控系統警報的例外情況 (exception)。
蒐集 Azure 登入憑證
惡意程式同樣設計了一套完整的搜刮系統來蒐集 Azure 的登入憑證,並且鎖定了 Azure 所有的驗證方法。這套系統首先會先定義一系列重要的 Azure 環境變數,然後有系統地檢查這些變數是否存在於處理程序所在的環境中。登入憑證供應者內含智慧解析邏輯,可處理複雜的組態設定,例如分號分隔的租戶清單與布林 (boolean) 旗標轉換。
惡意程式會以詳細的格式記錄找到的環境變數,讓駭客掌握已駭入的系統上有哪些可用的認證方法。
惡意程式設計了一套連環的憑證擷取系統來嘗試各種 Azure 認證方法。這套方法首先會試圖利用用戶端機密來擷取 Service Principal (服務主體) 登入憑證 (自動化系統最常用的認證方法),這需要一個租戶 ID、用戶端 ID 和用戶端機密。如果這三項資料存在,那麼惡意程式就會實例化一個 ClientSecretCredential 物件來向 Azure Resource Manager 認證,並使用服務主體 (service principal) 所被授予的權限來存取 Azure 資源。每個成功擷取到的登入憑證都會跟著租戶 ID 和用戶端 ID 儲存到記錄檔內。
如果使用用戶端機密來認證時失敗,惡意程式就改回使用憑證來認證,並檢查是否有 AZURE_CLIENT_CERTIFICATE_PATH 及 AZURE_CLIENT_CERTIFICATE_PASSWORD。
接著,惡意程式會嘗試透過使用者名稱和/或密碼來認證,不過程式碼內含有警告訊息指出這方法已經過時。
此外,惡意程式還可擷取 Workload Identity 登入憑證,這是一套現代化 Azure 驗證機制,主要用於 Kubernetes 環境。惡意程式鎖定了三個關鍵的環境變數:AZURE_TENANT_ID、AZURE_CLIENT_ID 以及 AZURE_FEDERATED_TOKEN_FILE。
此外,惡意程式還會竊取 Azure Pod Identity 的登入憑證,這是一套至今仍廣泛使用的老舊系統,用來提供 Azure 身分給 Kubernetes Pod。
惡意程式會檢查 AZURE_POD_IDENTITY_AUTHORITY_HOST 環境變數是否存在,也就是提供受管理身分權杖的 Instance Metadata Service (IMDS) 端點。當這個變數存在時,惡意程式會將一個標準的金鑰路徑附加在授權主機後面來建構完整的 OAuth 權杖端點網址。
若這個環境變數未設定,那麼惡意程式就會改回使用標準的 Azure IMDS 端點。
透過雲端機密管理員
除了竊取靜態登入憑證之外,惡意程式還會使用偷來的雲端登入憑證存取雲端原生機密管理服務。
程式碼中含有針對 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。
Azure Key Vault
Azure 機密搜刮流程瞄準了 Microsoft 的 Key Vault 服務,它會先使用 DefaultAzureCredential 認證提供者來進行認證,它會依序嘗試多種認證方式 (環境變數、受管理的身分、Azure CLI 登入憑證)。一旦通過認證,惡意程式就會使用 Resource Manager API 來列出訂閱中的所有 Key Vault 資源,過濾出類型為 Microsoft.KeyVault/vaults 的資源。針對每一個找到的金庫 (vault),惡意程式都會實例化一個 SecretClient 然後逐一查看其所有機密,並擷取其數值。
使用 TruffleHog 來掃描機密
惡意程式使用了一套自動化 TruffleHog 部署系統,直接從 GitHub 下載、解壓縮以及快取最新版本的工具。在下載任何檔案之前,它會先檢查其快取內是否已經含有 TruffleHog 二進位檔案;如果沒有,就從 GitHub 擷取最新版本的資訊,然後針對各系統下載對應的二進位檔案,接著將檔案解壓縮之後,讓它變成可執行檔。
惡意程式會使用 GitHub API 來自動取得 TruffleHog 最新版本的資訊而不需經過認證,它會發送 GET 請求至 /repos/trufflesecurity/trufflehog/releases/latest 來取得 metadata 以及平台相關的下載連結。如此一來,就不需將版本編號寫死在程式內,同時也確保能與未來的更新相容。
惡意程式會使用 TruffleHog 的檔案系統掃描工具來掃描受害者的主目錄,尋找一些寫死的機密,呼叫該工具來協助解析機密內容並產生 JSON 輸出,也可使用選擇性參數來縮小或自訂掃描範圍。它會設定掃描時間為 10 分鐘,以避免長時間執行容易讓人起疑,並且同時擷取輸出和錯誤。掃描結果會封裝在一個結構化報告中,內含已發現的機密、錯誤、掃描持續時間,以及工具的結束碼,讓操作人員可以快速知道掃描的結果是成功、部分完成,或者失敗。
資料外傳
惡意程式建立了一套多重階段的資料外傳系統,將偷到的四種不同類別的資料彙整後上傳至惡意程式在受害者的 GitHub 帳號底下自動建立 (且由駭客掌控) 的 Git 儲存庫。資料外傳過程會產生四個不同的 JSON 檔案,每個檔案都含有竊取到的特定資料類別。
| 檔案名稱 | 說明 |
| contents.json | 彙整的系統 metadata,例如:平台詳細資訊、主機名稱、作業系統使用者資訊,以及 GitHub 認證狀態。 |
| environment.json | 蒐集到的所有環境變數、雲端登入憑證、API 金鑰、資料庫字串,以及其他敏感的組態設定資料。 |
| cloud.json | 內含從 AWS Secrets Manager、GCP Secret Manager 和 Azure Key Vault 蒐集到的機密,顯示惡意程式會使用偷來的登入憑證存取雲端原生機密儲存。 |
| truffleSecrets.json | 儲存 TruffleHog 的掃描結果,包括:寫死在原始程式碼中、組態設定檔案以及使用者主目錄內其他文件當中的機密。 |
經由供應鏈擴散
惡意程式會自動在受害者所維護的每一個 NPM 套件內植入後門,然後再重新發布套件,使它們在安裝期間執行惡意檔案。這樣就能建立一個像蠕蟲般散播的管道在 NPM 生態系內大量擴散,進而感染數千名信任該套件的下游使用者。
其攻擊過程分成五個階段:搜尋受害者擁有的所有套件、下載原始的 tarball 壓縮檔、注入惡意的預先安裝掛勾 (preinstall hook)、綁入惡意程式安裝程式,最後將修改過的套件重新發布成正常的更新。
這整套工作流程都能完全自動化,而且一次最多可平行處理 100 個套件,不僅發揮最大的繁殖力,還能盡量降低被偵測的機率。
取得維護人員的套件
惡意程式會向 NPM 登錄檔查詢來搜尋已通過認證的受害者所維護的所有套件。在實作中,它使用 NPM 的搜尋 API 搭配 maintainer 過濾條件來列出受駭帳號所擁有的套件。列出套件時會包括每個套件的詳細 metadata,其中就包含了下載統計資料,這可讓惡意程式挑選要優先鎖定的高衝擊目標。駭客會依據每月下載數量來將套件排序,優先入侵最熱門的套件以盡可能擄獲最多受害者。
搜尋 NPM 登錄
在搜尋時,惡意程式會使用 NPM 的官方搜尋 API 端點 (/-/v1/search) 並搭配適當的網址編碼與認證標頭。這樣就可以突破 20 個套件的預設搜尋限制,實際攻擊時,惡意程式最多會請求到 100 個套件。
下載與解壓縮套件
惡意程式會從 NPM 登錄下載原始的 tarball 壓縮檔來準備修改。如此可確保其後門化版本仍維持原本的所有功能,然後再加入惡意功能。下載程序會使用標準的 HTTP 擷取與壓縮方式 (gzip、deflate、brotli) 有效率地下載套件。暫存目錄的命名方式 (NPM-update-) 會仿照正常 NPM 工具的作法來避免被處理程序監控系統偵測。
注入預先安裝掛勾
其供應鏈攻擊的核心是修改套件的 package.json 檔案來注入惡意的預先安裝腳本。這個腳本會在任何人安裝此 NPM 套件時自動執行 (甚至在該套件實際用到的相依元件安裝之前)。
預先安裝腳本會在 NPM 安裝過程中最早的階段執行,確切來說就是:
- 在相依性解析之前。
- 在套件安裝之前。
- 在任何正常的套件程式碼執行之前。
- 擁有使用者的完整權限。
綁入惡意檔案
惡意程式會綁入一個精密的多重階段安裝腳本 (setup_bun.js) 來處理 Bun 執行時期環境的安裝以及惡意程式的執行,如本文一開始所提到。該腳本在設計上可同時支援 Windows、Linux 和 macOS 平台。
重新發布套件
惡意程式在注入惡意程式碼並綁入惡意檔案之後,就會重新包裝修改過的套件,接著使用受害者的登入憑證將套件發布到 NPM。發布程序所使用的 NPM 認證權杖是在先前的攻擊中從受害者取得的,這樣做可以讓這個惡意更新看起來像受信任的套件維護人員所發布。版本編號更新 (增量修補) 會依循版本編號的編排習慣,讓它看起來像是一次錯誤修正或小幅更新,這樣使用者在安裝時通常就不會仔細檢查。
採用 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)
入侵指標請參閱此處。