你今早照常 npm install,幾秒鐘後終端機安安靜靜回到提示號。你沒多想,繼續寫 code——但你剛才安裝的那個套件,或許已經在你的 CI 環境裡種下了一整套間諜軟體。

這不只是杞人憂天。就在 2026 年 4 月 23 日,Socket 安全研究團隊揭露:開源密碼管理器 Bitwarden 的 CLI 版本(2026.4.0)已被 Checkmarx 供應鏈攻擊活動感染。這個被安全界形容為「近年來規模最大的供應鏈攻擊」的事件,已經從 Checkmarx 自家的 MCP Server 擴散到 Bitwarden 這樣擁有超過 1000 萬使用者的基礎設施級工具。

這篇文章將從技術細節切入,完整還原攻擊的全貌——不是為了嚇你,而是讓你看清楚供應鏈攻擊的運作方式,才知道如何保護自己。

不是單一事件,而是正在進行中的大規模活動

要理解 Bitwarden 被入侵的嚴重性,必須先看懂它背後的 Checkmarx 供應鏈活動。

Socket 團隊在 4 月 22 日首次揭露 Checkmarx(一家知名的應用安全測試公司)的內部 MCP Server 套件被植入惡意程式碼。原本以為是針對 Checkmarx 的單一攻擊,但進一步分析後發現:攻擊者拿到的不只是一個 npm token,而是整條 CI/CD 管線的控制權。

攻擊者透過被入侵的 GitHub Action,在 Bitwarden CLI 的建置流程中植入惡意負載。這意味著你從 npm 官方 registry 下載的 @bitwarden/cli 2026.4.0,是經過「正規簽章流程」發布的——你看到的版本號、發布者身份、簽章,全部都是真的。這就是供應鏈攻擊最可怕的地方:你不是下載到假的套件,而是真的套件被餵了毒

所幸截至揭露為止,Bitwarden 的 Chrome 擴充功能、MCP Server 和其他合法發布渠道尚未受到影響。但如果攻擊者能突破 Bitwarden 這樣等級的 CI 防護,任何 npm 生態系的套件發布者都不能說自己絕對安全。

深入惡意程式碼:bw1.js 的技術拆解

Socket 研究團隊發現,被植入 Bitwarden CLI 套件中的惡意檔案名為 bw1.js。它與前一天在 Checkmarx MCP Server 中發現的 mcpAddon.js 共享了大量核心基礎設施,顯示這是同一組攻擊者(或至少使用同一套工具鏈)所為。

C2 通訊架構

惡意程式碼使用一個固定的命令與控制(C2)端點:audit.checkmarx[.]cx/v1/telemetry。這個 URL 會被 __decodeScrambled 函式進行模糊化處理,種子值為 0x3039。這種手法在 CTF 比賽中很常見,但在真實世界的 APT 等級攻擊中同樣有效——不是為了防專業逆向工程,而是為了繞過初階的自動化掃描規則。

資料外洩不僅透過 C2 端點,還包括:

程式碼結構與核心功能

bw1.js 的主程式寫在一個名為 bunny 的函式中,執行時會先透過 Bun v1.3.13 直譯器(從 GitHub releases 動態下載)來運行。選擇 Bun 而非 Node.js 並非偶然——Bun 的執行速度更快、啟動開銷更低,更重要的是在 CI 環境中不會留下明顯的 Node.js 進程痕跡。

內嵌的 gzip+base64 負載解壓後包含:

  1. Python 記憶體掃描腳本:專門針對 GitHub Actions Runner.Worker 進行記憶體傾印,目標是擷取執行中的 GitHub Token
  2. setup.mjs 載入器:用於在 npm 重新發布的套件中植入 preinstall hook
  3. GitHub Actions 工作流程 YAML:自動化的植入腳本,用於在受害者的 CI 環境中建立持久化
  4. 硬編碼 RSA 公鑰:用於加密外洩資料
  5. 意識形態宣言字串:這點後面會再提到

憑證竊取範圍:遠比想像中全面

bw1.js 的憑證竊取模組涵蓋了現代開發者常用的所有憑證類型:

目標 來源
GitHub Token Runner.Worker 記憶體掃描、環境變數
AWS 憑證 ~/.aws/ 檔案與環境變數
Azure Token azd 工具
GCP 憑證 gcloud config config-helper
npm 組態 .npmrc 檔案(包含發布用 token)
SSH 金鑰 ~/.ssh/ 目錄
環境變數 所有 CI 執行環境中的變數
Claude/MCP 組態 Claude Agent 設定檔

換句話說,如果你的 CI 環境中任何一個環節曾經暴露這些憑證,攻擊者都能一網打盡。

供應鏈擴散機制

最令人憂心的環節在於 npm token 的後續利用。攻擊者竊取到 npm token 後,會:

  1. 讀取該 token 對應的所有可寫入套件清單
  2. 將惡意 preinstall hook 注入這些套件
  3. 發布新版本到 npm registry
  4. 當其他開發者安裝這些被挾持的套件時,preinstall hook 自動執行,讓攻擊擴大

這個機制類似於經典的 typosquatting 攻擊,但更難防——因為被挾持的套件是原本就信任的相依套件,沒有任何異常的名稱或版本提示。

同時,攻擊者還會透過注入的 GitHub Actions workflow YAML 來竊取儲存庫的 secrets,進一步擴張攻擊範圍。

資料外洩手法

外洩的資料透過 GitHub API 寫入受害帳號下的公開儲存庫。這些儲存庫使用統一的命名規則:{word}-{word}-{3位數},主題靈感來自科幻小說《沙丘》中的 Shai-Hulud(沙蟲)。

加密後的結果被 commit 到這些儲存庫中,而 GitHub Token 則藏在 commit message 中,使用標記字串 LongLiveTheResistanceAgainstMachines 作為識別符號。

bw1.js 與 mcpAddon.js 的關鍵差異

雖然兩者共享大量基礎設施,但 bw1.js 新增了幾個值得注意的功能:

鎖定機制避免重複執行

硬編碼路徑 /tmp/tmp.987654321.lock 作為鎖定檔案,確保同一時間只有一個惡意實例在運行。這可以防止因 CI 任務並行執行而觸發多個實例導致的異常或偵測。

Shell Profile 持久化

惡意程式碼會將自身注入 ~/.bashrc~/.zshrc。這意味著即使駭客初始的 CI 管線入口被關閉,只要開發者在本地終端機開啟 shell,惡意程式碼就可能重新啟動。

品牌明確的意識形態色彩

與 Checkmarx 事件中試圖偽裝成合法組態儲存庫(描述為「Checkmarx Configuration Storage」)不同,Bitwarden CLI 惡意負載使用了明確的品牌命名:

這種差異暗示了幾個可能性:可能是同一個操作者使用不同的戰術,也可能是分裂派系使用共享基礎設施但帶有更強烈的意識形態動機。

俄語環境的靜默退出

像多數來自特定地區的惡意軟體一樣,bw1.js 內建了俄語 locale 的檢查。系統會檢查 Intl.DateTimeFormat().resolvedOptions().locale 和環境變數 LC_ALLLC_MESSAGESLANGUAGELANG,若偵測到以「ru」開頭的 locale 設定,則靜默退出而不執行任何惡意行為。

這對開發者意味著什麼

如果你是 Bitwarden CLI 使用者

Socket 團隊建議所有 Bitwarden CLI 的使用者立即:

  1. 檢查 CI 日誌中是否有 2026.4.0 版本的安裝記錄
  2. 輪替任何可能在受影響工作流程中暴露的 secrets
  3. 確認 npm lockfile 中的 @bitwarden/cli 版本,如有需要則鎖定到未受影響的版本

更重要的是,這起事件不該被當作「Bitwarden 自己的問題」——攻擊的本質是 CI/CD 管線入侵,任何使用 GitHub Actions 建置 npm 套件的組織都有類似的風險。

如果你是套件維護者

如果你是 npm 套件的維護者,以下是你可以立即採取的防護措施:

第一步:啟用套件發布的 MFA 強制驗證
在 npm 帳號設定中開啟 Two-Factor Authentication(2FA),並在套件設定中啟用 "requires2fa": true。這可以防止單純的 token 竊取就被用於發布。

第二步:限制 GitHub Actions 的權限範圍
檢查你的 .github/workflows/ 中的所有 workflow,使用最小權限原則。如果一個 workflow 只需要讀取權限,就不要授予寫入權限。

permissions:
  contents: read
  packages: read

第三步:隔離發布流程
將套件建置與發布流程分離在不同的環境中。建置用一般的 CI runner,發布則使用專用、隔離的環境,並需要人為確認。

第四步:監控被動的 GitHub 活動
設定 GitHub 通知或使用第三方監控工具,當你的帳號在非預期時間或 IP 下建立了新的公開儲存庫時,立即收到警報。

如果你是企業安全管理者

對企業而言,這次事件更值得關注的是供應鏈監控的盲點。

許多企業的資安團隊會掃描正式上線的伺服器,但 CI/CD 管線往往是監控的灰色地帶——因為開發環境的變動頻繁、套件更新快速,傳統的靜態掃描工具很難涵蓋所有相依關係。

企業可以考慮以下做法:

  1. 導入相依性行為監控:不只是掃描已知漏洞(CVE),而是監控套件的執行行為是否異常。Socket 的工具在這方面就是一個例子。
  2. 建立 CI/CD 管線的異常行為基線:記錄正常的 CI 執行模式(執行時間、對外連線目標、建立的檔案),並在出現異常時自動阻斷。
  3. 限制 CI runner 的網路出口:不讓 CI runner 能任意連線到外部端點。建立允許清單,阻止惡意程式碼與 C2 端點通訊。

Checkmarx 事件的更大圖景

把 Bitwarden CLI 事件放回 Checkmarx 供應鏈攻擊的脈絡中來看,我們可以畫出更完整的攻擊鏈:

Checkmarx MCP Server (初始入口)
       ↓
   npm token 外洩
       ↓
   Bitwarden CLI CI/CD 入侵
       ↓
   GitHub Token、npm Token、雲端憑證被竊
       ↓
   被挾持套件重新發布 → 攻擊擴散到更多下游

這個攻擊鏈最可怕的地方在於它的遞迴性:每次成功的入侵都會產生新的憑證,而新的憑證又可以攻擊更多目標。從 Checkmarx 到 Bitwarden,再到所有 Bitwarden 的使用者,攻擊半徑不斷擴大。

更令人擔憂的是,攻擊者的工具鏈已經高度自動化。從記憶體掃描、憑證擷取、資料加密外洩、到套件重新發布,每一個步驟都是自動完成的。攻擊者不需要手動操作,只要 CI runner 的環境符合條件,整個攻擊流程就會自動跑完。

觀察家結語

供應鏈攻擊不是新名詞,但 Bitwarden CLI 這起事件把它帶到了新的層次——當一個擁有千萬使用者、經過安全審計、排名前三的密碼管理器都擋不住時,問題的根源已經不是單一公司或工具的安全性,而是整個 npm 生態系的信任模型出了問題。

攻擊者證明了兩件事:一是 GitHub Actions 的權限模型存在被大規模利用的可能,二是 npm 的 token 驗證機制在沒有 MFA 強制的情況下形同虛設。這不是技術上的失誤,而是設計上的漏洞——當你只需要一個字串就能發布套件時,供應鏈安全的基礎就是脆弱的。

對開發者社群來說,這是一個殘酷但重要的提醒:信任應該是動態的、可驗證的、最小化的,而不是靜態的、盲目的、全有全無的。你的套件管理器不會幫你擋這種攻擊,你的 CI 平台不會提醒你 runner 正在外送資料,你的密碼管理器反過來成了攻擊的傳播節點。

那麼,我們該怎麼辦?或許答案不在於更多昂貴的安全工具,而在於回歸最基本的安全原則:最小權限、零信任、持續驗證。如果你的 CI 不需要發送 npm publish 的權限,就不要給它。如果你的 token 不需要寫入公開儲存庫的能力,就限制它。如果一個套件不需要在安裝時執行腳本,就擋住它。

在供應鏈攻擊成為常態的時代,安全的關鍵不是找到完美的工具,而是養成習慣——習慣懷疑、習慣最小化、習慣檢查。不是因為你不信任任何人,而是因為你懂得信任應該建立在可驗證的基礎上。