新聞稿拆解

替你保管所有 AI 金鑰的那一層,被換成了竊密程式

2026 年 10 月 21 日,開源 LLM 閘道 LiteLLM 的 1.107.0 版在 PyPI 與 Docker 上架約兩小時,裡面多了三個檔案:每次 Python 啟動就跑的竊密程式,和一個每小時回頭拿指令的載入器。官方說入口是一套對外開放的內部 LiteLLM,攻擊者從它的日誌裡撿到工程師的 GitHub CLI 權杖。這篇拆開能證明的、只能推的、還沒人知道的,以及你今天該換哪幾把鑰匙。

30 秒看懂
  • 第一手有兩份:LiteLLM 官方的事件說明,和 Upwind 研究團隊在 GitHub 開的 issue(含 sha256、靜態解碼結果與 IOC)。兩者一致:PyPI 上 1.107.0 的 7 個 wheel 與 Docker 的 v1.107.0、latest、main-latest、main-stable 都被植入竊密程式;官方的 v1.107.0 原始碼標籤是乾淨的,有毒的版本是從仿冒儲存庫 Berri-AI/litellm 建出來的。
  • 竊密程式在每次 Python 啟動時執行,拿走瀏覽器密碼、SSH 金鑰、雲端與 Kubernetes 憑證、AI 工具設定、每個行程的環境變數;如果有 DATABASE_URL,還會直接撈 LiteLLM 資料庫裡存的上游供應商金鑰。Wiz 在外傳後約 4.5 小時,看到被偷的 AWS 執行角色憑證被拿去查 Bedrock 能用哪些模型。
  • 官方說入口是一套對外開放的內部 LiteLLM:攻擊者從它的日誌裡拿到三位工程師的 GitHub CLI 權杖,其中一把屬於組織擁有者,用它關掉分支保護、改發布流程。官方也寫,某些情況下 Claude Code 會把 GitHub CLI 權杖帶進回應內容,而回應內容被記進了日誌。這段官方標的是「相信」,還沒確認。

發生了什麼

想像公司有一個保險櫃,所有供應商的提款卡都鎖在裡面,員工要用就跟櫃子借。某天保險櫃廠商寄來一個「新版鎖頭」,大家照常換上。這個鎖頭會照常開關,只是每次轉動時,都會把櫃子裡每一張卡的卡號抄一份寄出去,順便也抄走開櫃那個人口袋裡的鑰匙。

LiteLLM 就是很多團隊的那個保險櫃。它是一個開源的 LLM 閘道(gateway)與代理(proxy):應用程式不直接拿 OpenAI、Anthropic、Azure、Bedrock 的金鑰,而是統一打 LiteLLM,由它保管上游金鑰、分配虛擬金鑰、記錄用量。2026 年 10 月 21 日 11:39 UTC(台灣時間 19:39),PyPI 上出現了 litellm 1.107.0;7 分鐘後,GHCR 與 Docker Hub 上的 v1.107.0、latest、main-latest、main-stable 也指向同一個新映像。這個版本裡多了三個檔案,裝起來之後,那台機器上每一次 Python 啟動,都會把它碰得到的憑證打包寄給攻擊者。

先講清楚第一手文件的性質。這次有兩份第一手:一份是 LiteLLM 官方的事件說明,列出受影響的版本、時段、時間軸與官方相信的攻擊鏈;另一份是資安公司 Upwind 的研究團隊在 GitHub 上開的 issue,附上每個檔案的 sha256、純靜態解碼(沒有執行)的結果與 IOC,維護者也在同一串裡每兩小時更新一次狀態。這兩份大部分可以互相核對;官方自己標「相信、尚未確認」的段落,本篇會另外標出來。這篇的線索來自 Will 保哥的技術交流中心在 Facebook 的提醒貼文,內容一律以上面兩份第一手為準。

官方事件說明重點(LiteLLM,最後更新 2026-10-21 20:30 UTC)READ-ONLY
受影響的版本與時段
- PyPI:litellm==1.107.0,全部 7 個 wheel,11:39:51 至 13:23 UTC 可下載
- Docker Hub:v1.107.0、latest、main-latest、main-stable
指向受感染映像 sha256:1bbcb1443bc9...,11:46:56 至 14:20 UTC
- GHCR:同四個標籤,11:46:56 UTC 起,13:51 UTC 前已不可取得
- 官方 v1.107.0 原始碼標籤不含惡意檔案;有毒版本由攻擊者控制的儲存庫建置

誰受影響
- 上述時段內 pip 安裝未固定版本的 LiteLLM
- 上述時段內使用這四個 Docker 標籤
- 企業版私有套件庫不受影響

建議
- 暫時固定到 1.106.2 或更早、明確指定的版本
- 不要先跑 pip install:受感染環境裡任何 Python 啟動都會再執行一次惡意檔案

把兩份第一手與 Wiz 的報告排成一條線。時間以 UTC 為準,台灣時間加 8 小時。

  1. 2026-03-24
    LiteLLM 上一次遭供應鏈攻擊:1.82.7 與 1.82.8 被植入惡意程式,在 PyPI 上約 46 分鐘;官方說之後大幅強化了發布流程
    Arthur、Trend Micro 當時的分析;官方 10-21 說明提到「三月那次事件之後」
  2. 2026-10-13
    外傳網域 litellm.dev 註冊;GitHub 帳號 letters9813 建立,之後用來放 dead-drop 檔案
    Upwind issue(RDAP、GitHub API);Wiz IOC
  3. 2026-10-19(週一)
    官方為了讓兩位內部工程師每天替客戶發修補,暫時擴大了發布權限
    官方事件說明「How it was undermined」第 1 點
  4. 2026-10-21 08:56–10:46
    攻擊者在仿冒儲存庫 Berri-AI/litellm 推了 10 個提交:先加入三個惡意檔案、把版本號改回 1.107.0,再在 pyproject.toml 加一行把惡意檔案打包進 wheel,最後改 Dockerfile 把它們複製進映像
    Upwind issue 逐筆列出 sha 與時間;Wiz 報告同樣列出
  5. 2026-10-21 10:08
    分支保護擋下一次對私有發布流程的未授權修改;5 秒後,一把屬於高權限開發者帳號的憑證被用來覆蓋保護、套用修改
    官方時間軸 10:08:29 與 10:08:34
  6. 2026-10-21 10:46–11:11
    被改過的流程以仿冒儲存庫為來源跑完測試環境;正式發布流程啟動後,第二個開發者帳號的憑證被用來通過正式環境的核准關卡
    官方時間軸;映像的建置來源證明(provenance)也記錄來源是 Berri-AI/litellm
  7. 2026-10-21 11:39–11:47(台灣 19:39–19:47)
    7 個惡意 wheel 上傳到 PyPI;同一次發布的容器映像推上 GHCR 與 Docker Hub,浮動標籤一起指過去
    官方時間軸;Upwind issue 的 registry 查核
  8. 2026-10-21 11:53
    第三個開發者帳號的憑證被用來刪除正式與測試流程的執行紀錄,可供調查的日誌因此變少
    官方時間軸
  9. 2026-10-21 12:06(台灣 20:06)
    Upwind 研究團隊在 GitHub 開 issue 通報,附 sha256、靜態解碼結果與給使用者的處置步驟
    GitHub issue #47826
  10. 2026-10-21 13:23–14:20
    PyPI 移除 1.107.0;GHCR 標籤 13:51 前不可取得;Docker Hub 14:20 全部移除或改指向乾淨映像。維護者 13:50 在 issue 宣告 SEV-0
    官方時間軸;issue 內維護者留言與第三方 registry 查核
  11. 2026-10-21 16:20–17:01
    Wiz 觀察到攻擊者從兩個 IP 驗證從 CI runner 與 Kubernetes 節點偷來的 AWS 執行角色憑證:先 GetCallerIdentity,再查 Bedrock 能用哪些模型;少數環境還被列舉了 IAM 使用者、角色與金鑰
    Wiz 報告「Post-Exploitation Activity」
  12. 2026-10-21 16:32–21:22
    官方陸續公布:動作來自三位工程師被偷的 GitHub CLI OAuth 權杖;權杖來源相信是一套對外開放的內部 LiteLLM 的日誌
    issue 內維護者狀態更新;官方事件說明
  13. 2026-10-21 23:37
    官方開始全組織清點並撤銷或輪替所有驗證權杖;套件庫仍鎖住新版本,一般版本發布暫停
    issue 內維護者狀態更新
  14. 未公開
    內部 LiteLLM 怎麼被進入、有多少環境裝過或拉過、攻擊者是誰、和三月那次有沒有關係
    截至 2026-10-22 上午(台灣時間),官方與研究者皆未公開

從上架到 PyPI 移除約 1 小時 44 分鐘,Docker Hub 則掛了約 2 小時 33 分鐘。時間不長,但浮動標籤的意思就是「每次拉都拿最新的」:這段時間內任何自動部署、CI 建置、Kubernetes 重新排程,都會自己把它拉進來。

怎麼發現

不是 LiteLLM 自己發現的。Upwind 的研究團隊在 1.107.0 上架約 27 分鐘後開了 issue,標題直接寫「compromised」。他們的做法值得記一下:把 7 個 wheel 的內容和 v1.107.0 原始碼標籤逐檔比對,發現 wheel 多了三個檔案,而且原始碼標籤裡根本沒有它們;再把惡意檔案純靜態解碼,沒有執行,就讀出了它會偷什麼、寄去哪裡。後續他們又比對了映像附帶的建置來源證明,看出這一版、而且只有這一版,是從名字多一個連字號的 Berri-AI/litellm 建出來的。

維護者在通報後約 1 小時 44 分鐘回應並宣告 SEV-0,之後每兩小時在 issue 與官方部落格同步更新。從通報到 Docker 全部下架,大約 2 小時 14 分鐘。

攻擊者怎麼做到

到這裡,其實已經足夠理解整起事件。如果你關心的是該怎麼避免,可以直接往「如何避免」繼續閱讀。如果你想知道公開資料能還原到哪一步,下面開始進入技術拆解。

白話結論:攻擊者沒有去改 LiteLLM 的原始碼,也沒有偷 PyPI 的發布密碼。他拿到幾位工程師的 GitHub 權杖,把官方自己的發布流程改成去讀他的儲存庫;於是有毒的版本經過官方的流水線、帶著官方的簽章上架。

一條加固過的發布流程,怎麼被走完

官方說三月那次之後,他們把發布搬到私有儲存庫、用固定的工作流程與 Trusted Publishers 發布、讓 Docker 發布標籤不可變更、替映像簽章,而且「發布只能由一個綁在辦公室裡某台筆電上的帳號執行」。這些控制都還在,這次是被一層一層繞過去的。

照官方的說法排一次:週一暫時擴大了發布權限;攻擊者手上有三位工程師的 GitHub CLI 權杖,其中一把可以跑工作流程,另一把屬於組織的兩位擁有者之一。他先試著提交「讓發布流程可以指定其他來源儲存庫」的修改,被分支保護擋下;接著用擁有者的權杖關掉分支保護、強推修改;再跑被改過的流程,來源指向 Berri-AI/litellm;正式發布需要另一個人核准,他就用第二個帳號的權杖核准;發布完,再用第三個帳號刪掉執行紀錄。

每一道關卡都在,但每一道關卡驗證的是「這個帳號有沒有權限」,而三個帳號的權杖都在攻擊者手上。兩人核准制的前提是兩個人是不同的人;當兩把權杖都被同一個人拿著,它就只是同一個人按兩次。

三個檔案,兩種自動執行

Python 有兩個鮮少有人注意的機制:site-packages 裡的 .pth 檔,以及名為 sitecustomize 的模組,都會在直譯器啟動時自動載入,不需要任何程式 import 它們。這是 Python 官方文件寫明的正常行為,原本是給套件設定路徑用的。

這次的三個檔案就是利用這一點。cook.pth 只有一行,讓 cook.py 在每次 Python 啟動時被載入;cook.py 是包了壓縮與 base64 的竊密程式,在背景執行緒跑一次,偷完寫一個標記檔;sitecustomize.py 則會分岔出一個脫離終端機的背景程序,每小時去讀 GitHub 上一個檔案,如果檔案裡寫了網域,就從那個網域下載程式並執行。那個檔案從 10 月 20 日起一直是空的,GitHub 也在 10 月 21 日下午把儲存庫下架,所以第二段從來沒有被啟動過;但在下架前,攻擊者只要改一行字,就能讓每一台中招的主機開始聽他的指令。

Wiz 指出,同時用 .pth 與 sitecustomize 兩種方式,是第一個被公開記錄的野外案例。三月那一次只用了 .pth。

這裡也是官方特別提醒「不要先跑 pip install」的原因:pip 本身就是一支 Python 程式,在受感染的環境裡跑 pip,等於再觸發一次竊密。

深入閱讀對照 MITRE ATT&CK:攻擊鏈的每一格都有文字,只有最前面那一格標著「相信」內部 LiteLLM 怎麼被進入,官方仍在確認
發生了什麼 · MITRE ATT&CK T1190
官方相信:攻擊者進入一套對外開放的內部 LiteLLM(裡面有一個名字看起來無害、不該存在的使用者帳號),懷疑是透過一個最近才修補的漏洞。官方自己標為尚未確認。
當時留下的訊號
內部工具的使用者清單出現沒人認得的帳號;內部服務對公網開著。
能擋下它的控制
內部 AI 閘道不對公網開放;使用者帳號定期盤點;修補後回頭查修補前有沒有人進來過。

六格裡五格有官方或研究者的文字可以對上;第一格與最後一格標缺口,一個是入口還沒確認,一個是損失還沒有人公開。

這次外洩了什麼

這次不是一家公司的資料外洩,而是「每一台裝過的主機,各自外洩了自己身上所有的東西」。下表依 Upwind 的靜態解碼與 Wiz 的分析逐項列出竊密程式會拿的憑證;最後一列是 LiteLLM 自己那一端被拿走的。你的環境實際被拿走哪些,取決於那台主機上有什麼。

leaked credentials這次外洩了什麼:服務、用途、存在形式
服務憑證類型用途存在形式放在哪權限範圍狀態
LiteLLM Proxy 資料庫裡的上游供應商金鑰(OpenAI、Anthropic、Azure OpenAI、AWS Bedrock、Google Vertex/Gemini、Cohere 等)API 金鑰LiteLLM 代替應用程式呼叫各家模型資料庫資料表 LiteLLM_CredentialsTable 與 LiteLLM_ProxyModelTable主機若設了 DATABASE_URL,竊密程式會直接查詢這兩張表這是 AI 閘道特有的損失:一台主機中招,所有團隊共用的上游金鑰一次全走官方要求全部輪替
LiteLLM master key 與虛擬金鑰管理金鑰與發給各應用的虛擬金鑰管理閘道、讓應用程式透過閘道呼叫模型環境變數、設定檔、資料庫閘道主機拿到 master key 等於拿到閘道的管理權官方要求輪替
LiteLLM 使用的資料庫DATABASE_URL 裡的帳號密碼閘道連線資料庫環境變數裡的連線字串閘道主機與每個行程的環境除了上游金鑰,用量、使用者與日誌都在這個資料庫官方要求輪替
雲端執行身分(AWS EC2 執行角色、ECS 任務角色、GCP 服務帳號、Azure 受控識別)暫時憑證與存取權杖主機或容器存取雲端資源執行個體中繼資料服務(IMDS)雲端主機、容器、Kubernetes 節點、CI runner竊密程式拿到 AWS 憑證後會先用 STS GetCallerIdentity 驗證;Wiz 已觀察到這些憑證被拿去查 Bedrock已被試用(Wiz)
開發者與 CI 的雲端、叢集、程式碼平台憑證AWS、Azure、gcloud、kube、docker、gh 的設定與權杖;.git-credentials、.npmrc、.pypirc 與 maven、gradle、cargo 等套件庫憑證;Kubernetes 服務帳號權杖、/run/secrets、Vault、Terraform部署、發布套件、存取叢集家目錄下的設定檔、掛載的密鑰檔開發者筆電、CI runner、容器其中套件發布憑證最危險:拿到它,就能對你的使用者重演一次同樣的攻擊未知,依主機而定
AI 工具的憑證與設定(Claude、Codex、Gemini、Qwen、Cursor、Windsurf、Kiro、VS Code MCP、Copilot、Hugging Face)API 金鑰、OAuth 權杖、MCP 伺服器設定開發者使用 AI 代理與編輯器家目錄下各工具的設定檔開發者筆電、裝了 AI 工具的 CIMCP 設定裡常帶著其他服務的權杖未知,依主機而定
每個可讀行程的環境變數任何放在環境變數裡的密鑰各種服務/proc 底下每個行程的環境同一台主機上、權限讀得到的所有行程不只是 LiteLLM 自己的環境變數,而是整台主機上其他服務的官方要求輪替
SSH、GPG、TLS 私鑰與瀏覽器資料~/.ssh 下的金鑰、GPG 私鑰、/etc/ssl/private 與 Let's Encrypt 私鑰;Chromium 與 Firefox 的已存密碼、Cookie登入主機、簽章、網站憑證、各種網站的登入狀態檔案開發者筆電與伺服器瀏覽器 Cookie 可以直接接管已登入的工作階段未知,依主機而定
LiteLLM 公司端:三位工程師的 GitHub CLI OAuth 權杖OAuth 權杖開發者用 gh 指令操作 GitHub依官方說法,出現在內部 LiteLLM 的日誌裡一套對外開放的內部 LiteLLM 部署其中一把屬於組織擁有者,足以關掉分支保護官方已撤銷
憑證本身已遮蔽或失效;這裡只說明它是哪個服務、拿來做什麼、以什麼形式存放,讓你能對照自己的環境。

第一到四列與第七列標證明,因為官方的處置清單明確點名要輪替它們,Upwind 也在解碼結果裡讀到對應的程式邏輯;第五、六、八列是竊密程式「會去找」的東西,實際有沒有被拿走,看那台主機上有沒有。最後一列標推論,是因為權杖的來源官方自己還在確認。

他們怎麼處理

照官方與 issue 的公開動作排一次。通報後約 1 小時 44 分鐘,維護者在 issue 宣告 SEV-0,並承諾每兩小時更新;PyPI 先隔離了 1.107.0,官方再把 GHCR 與 Docker Hub 的標籤移除或改指向乾淨映像。下午確認動作來自三位工程師被偷的 GitHub CLI 權杖後,官方撤銷了相關權杖,把 GitHub 組織暫時縮到只剩一位未受影響的成員,並移除套件庫對 GitHub 組織的信任連結,避免攻擊者再發一次版。晚上官方發布事件說明、公開相信的攻擊鏈;深夜開始清點全組織所有驗證權杖並輪替,套件庫持續鎖住新版本,一般版本發布暫停。

給使用者的處置,官方直接沿用 Upwind 寫的步驟:先用 shell 刪檔、殺背景程序,再固定版本重裝,最好整個環境重建;能連外的主機一律當成已外傳,輪替主機碰得到的每一把憑證;最後去 CloudTrail 查安裝前後執行角色的 GetCallerIdentity,以及這些憑證有沒有從你的基礎設施以外被使用。

哪裡做對,哪裡做錯

做對的地方有三個。第一,通報者與維護者的配合很快:通報後兩個多小時,所有受感染的發布管道都下架了。第二,官方把相信的攻擊鏈連同自己的盲點一起公開,包括擴大發布權限、擁有者權杖可以關掉分支保護、內部部署對公網開放,沒有只寫「遭第三方入侵」。第三,給使用者的處置寫到「不要先跑 pip install」這種細節,這是會實際救到人的一句話。

該檢討的有三層。第一層是內部工具對外開放。一套存著 AI 工作流量日誌的內部閘道,本身就是憑證倉庫;它不應該在公網上,裡面也不該出現沒人認得的帳號。第二層是日誌記錄了完整的回應內容。AI 代理的輸入輸出裡,什麼都可能出現;閘道把它們原樣存下來,就等於把開發者桌上的鑰匙一把一把收進同一個抽屜。第三層是權限的「暫時」。週一為了方便暫時擴大的發布權限,兩天後就成了攻擊鏈的一環;擁有者帳號的權杖放在日常開發的工具裡,讓分支保護形同虛設。這些都不是 LiteLLM 獨有的問題,而是任何一個用 AI 代理寫程式、又自架 AI 閘道的團隊都可能有的形狀。

如何避免

這一節寫給兩種人:用 LiteLLM 或其他開源 AI 閘道的團隊,以及任何用 pip、npm、Docker 浮動標籤部署東西的團隊。

今天就要做的,是確認自己有沒有中。不要先啟動 Python:在每台可能裝過 LiteLLM 的主機與 CI runner 上,用 find 找 cook.pth 與 litellm-1.107.0.dist-info;用 docker images --digests 比對官方公告的受感染 digest;翻 lockfile 與 requirements 看有沒有 1.107.0,也看有沒有沒固定版本的 litellm 與 latest 標籤。只要有任何一台中,照官方步驟先刪檔、殺背景程序,然後重建環境,再輪替:LiteLLM 資料庫裡的每一把上游金鑰、master key 與虛擬金鑰、DATABASE_URL 的密碼、主機的雲端執行角色、主機上所有行程環境變數裡的密鑰;開發者筆電中招的,SSH 金鑰、GitHub 權杖、套件發布憑證、AI 工具的金鑰與瀏覽器登入都要換。

這一週要改的是固定版本。正式環境的 Python 套件用 lockfile 固定版本與雜湊(pip 的 require-hashes、uv 或 poetry 的 lock),Docker 映像用 digest 而不是 latest、main-stable 這類浮動標籤;新版本出來,不要第一時間自動升,留一兩天給社群先踩。CI runner 與伺服器的出向流量改成白名單,竊密程式就算跑起來,也寄不出去。

這一季要處理的是 AI 閘道本身。它集中保管所有上游金鑰,就要用保險櫃的標準看它:不對公網開放;不記錄完整的請求與回應內容,或記錄前遮蔽權杖格式的字串;上游金鑰設用量上限與帳單告警;雲端執行角色只給 Bedrock 需要的那幾個動作。開發者這邊,GitHub 權杖要有期限、範圍最小,組織擁有者的帳號不拿來日常寫程式;AI 代理的對話紀錄與日誌,當成憑證來保管。

這篇提到的項目要花多少錢

LiteLLM 開源版免費,這次的成本是輪替的人力,以及被偷的金鑰萬一被拿去用的帳單。下表除了官方定價,其餘都是本站的估算或對照。

cost確認、輪替、和來不及輪替,各要花多少
項目用量單價期間US$NT$舉證
LiteLLM 開源版自架開源,US$0持續0.000PyPI 與 GitHub 免費取得;企業版另計
確認是否中招(主機、CI、映像、lockfile)
估:主機與 CI 越分散越久
假設 1 人 × 半天估 NT$5,000/人日一次78.002,496本站估算
中招後的全面輪替(上游金鑰、雲端角色、資料庫、開發者憑證)
估:每把金鑰要找出所有用到它的地方再換,換錯一處就停機;本站另一篇〈換一把金鑰,要改六個地方〉有實際清單
假設 2 人 × 2 天估 NT$5,000/人日一次625.0020,000本站估算
重建受感染的容器映像與虛擬環境依環境數量人力與 CI 分鐘數一次——無法通用估算
對照:一把外流的 AI 金鑰被拿去用,一天能燒多少
對照用,不是本次事件的金額;這次被偷的是整個閘道的上游金鑰,數量可能是好幾把
本站 2026-10-02 一把 Gemini key實際帳單單日3909.00125,088本站拆解〈App 從來沒上架,帳單先衝上來了〉
Bedrock 被偷用的費用
Wiz 只看到列出模型的動作,是否已實際叫用、用了多少,未公開
依攻擊者叫用的模型與量依模型每百萬 token 計價直到憑證被撤銷——AWS Bedrock 定價頁
對照:資料外洩的平均總成本
對照用,不是本次事件的金額
全球受訪組織平均US$4.44M/起2025 年度報告——IBM Cost of a Data Breach Report 2025
合計(僅可證明列)3909.00125,088
匯率假設 1 USD ≈ NT$32。單價為公開牌價或帳單實付,來源:AWS Bedrock 定價頁、本站 2026-10 Gemini 事件帳單、IBM 2025 報告、本站估算(2026-10)。「舉證」欄標明每筆損失的證據來源與等級( 可證明 推論 缺口)。

這張表要看的是第三列和第五列的關係:全面輪替兩個人兩天,大約是一把 AI 金鑰被拿去燒一天的六分之一。輪替很麻煩,但比不輪替便宜。

你可以學到什麼

站在攻擊者那一側看,這是一筆很划算的投資。他不需要找到 LiteLLM 的漏洞,也不需要偷 PyPI 密碼;他找到一套開著的內部閘道,從日誌裡撿到幾把開發者權杖,就借用了一整條加固過的官方發布流程,連簽章都是官方幫他簽的。而他挑 LiteLLM,是因為它的使用者正好是一群手上握著最多 AI 金鑰與雲端權限的人;竊密程式甚至寫好了去哪一張表撈上游金鑰。拿到之後,第一件事是驗證雲端憑證、查 Bedrock 能用哪些模型。

跟我們的防暴筆記對照

最直接的對照是本站 10 月 16 日的拆解〈51 個 Claude Code 代理沒讓他被抓,讓他被抓的是一個開著的資料夾〉。那篇的結論是「AI 代理的工作紀錄就是憑證庫」,主角是一個沒把自己的 AI 紀錄當憑證看的攻擊者;這次角色反過來,是一家防守方的 AI 閘道日誌裡躺著開發者的 GitHub 權杖,被攻擊者撿走。6 月那篇〈CLI 把整個服務的密鑰都印進了對話紀錄〉講的是同一件事的上游:給 AI 用的工具一旦把密鑰印出來,它就會出現在對話紀錄、閘道日誌、任何一個記錄回應的地方。

第二個對照是〈第二次被打穿的,是上一次沒換的那幾把〉。LiteLLM 和那家部署平台一樣,今年第二次出事;兩家都在第一次之後加固了,第二次都是從加固清單以外的地方進來。LiteLLM 這次在深夜開始「清點全組織每一把權杖」,正是那篇建議的做法:不是只換這次被用的,而是把能找到的全部換掉。

第三個對照是我們自己的 INC-2026-0827(排程回報「找不到帳戶」,被停權的是整個專案)與 10 月的 Gemini key 帳單事件。那兩次都是一把權限太大、沒人盯的金鑰被拿去用,帳單或停權通知才是第一個訊號。這次攻擊者拿到雲端憑證後第一件事就是查 Bedrock,代表 AI 額度本身已經是他的變現目標;那一次教我們的「AI 金鑰要設用量上限與帳單告警」,這次一樣用得上。至於鑰匙該放哪裡,我們在〈被偷三次之後,我們決定換一種方式放鑰匙〉寫過自己的選擇:不讓明文金鑰躺在設定檔、環境變數與日誌裡,竊密程式來了能拿的就少很多。

攻擊者這次的盤算
  • 找一套對外開著的內部 AI 閘道,日誌裡就有開發者的權杖。
  • 用擁有者權杖關掉分支保護,借官方的流水線與簽章發布,不用自己偷發布密碼。
  • 挑一個使用者手上握著最多 AI 金鑰與雲端權限的套件,竊密程式寫好去哪張表撈上游金鑰。
  • 拿到雲端憑證先驗證、查 Bedrock 能用哪些模型,能用的就是免費算力。
lessons本案的 4 個教訓
  1. 01
    AI 閘道集中保管所有上游金鑰,它的一次失守就是全部金鑰一次失守。用保險櫃的標準看它:不對外、不記錄明文、每把金鑰有用量上限。
  2. 02
    官方簽章與建置來源證明能證明「是誰的流水線建的」,不能證明「建的東西對不對」。要看的是來源儲存庫是不是你預期的那一個。
  3. 03
    兩人核准制的前提是兩個人是不同的人。開發者權杖放在日常工具與日誌裡,任何一道「需要權限」的關卡都可能被同一個人用兩把權杖走完。
  4. 04
    受感染的 Python 環境裡,連 pip 都會觸發惡意程式。先用 shell 確認與清理,再重建環境,最後輪替;順序錯了,就是再被偷一次。
keywords重點學習:最需要學習的 10 個關鍵字10 個
  1. 01
    供應鏈攻擊(supply chain attack)
    不直接打你,而是打你信任的套件或工具;這次是透過 LiteLLM 的官方發布流程。
  2. 02
    LLM 閘道(LLM gateway/proxy)
    替應用程式集中保管各家 AI 金鑰並轉發請求的那一層;這次的竊密程式會直接撈它的資料庫。
  3. 03
    .pth 檔
    site-packages 裡的 .pth 檔會在 Python 啟動時自動載入;cook.pth 就是靠這個讓竊密程式每次都跑。
  4. 04
    sitecustomize
    Python 啟動時自動匯入的模組;這次被用來放每小時回頭拿指令的載入器。
  5. 05
    浮動標籤(floating tag)
    latest、main-stable 這類會跟著新版本移動的映像標籤;這次一起被指向受感染映像。
  6. 06
    固定 digest(pin by digest)
    用映像內容的雜湊而不是標籤來指定版本;官方與研究者都建議改用這種方式。
  7. 07
    建置來源證明(build provenance)
    記錄映像由哪個流程、從哪個儲存庫建出來;Upwind 就是從這裡看出來源是仿冒儲存庫。
  8. 08
    GitHub CLI OAuth 權杖
    gh 指令登入後保存的權杖;這次三位工程師的權杖被拿來改發布流程。
  9. 09
    LLMjacking
    偷用別人的雲端 AI 額度;Wiz 看到被偷的 AWS 憑證被拿去查 Bedrock 模型。
  10. 10
    dead-drop
    攻擊者放在公開服務上的指令檔;這次是 GitHub 上一個每小時被讀一次、一直是空的檔案。
try today今天就可以來試試約 15 分鐘

全部唯讀,不改任何設定;做完你會知道自己有沒有同樣的破口。

  1. 不啟動 Python,直接找有沒有裝過 1.107.0 或留下惡意檔案(唯讀)
    find / -xdev \( -name cook.pth -o -name 'litellm-1.107.0.dist-info' -o -name 'remote_code_loader.lock' -o -name '.pc_once_*' \) 2>/dev/null
    看到什麼代表什麼:沒有任何輸出最好。找到 cook.pth 或 litellm-1.107.0.dist-info,就是裝過;找到 /tmp 下的 .pc_once_ 開頭檔案,代表外傳已經成功。先不要在這台機器上跑 pip 或 python,照官方步驟用 shell 處理。sitecustomize.py 沒列進來,是因為有些 Linux 發行版本來就有一支,要另外看它在不在 site-packages 根目錄、內容是不是你認得的。
  2. 比對本機的 LiteLLM 映像 digest(唯讀)
    docker images --digests | grep -iE 'litellm|berriai'
    看到什麼代表什麼:digest 開頭是 1bbcb1443bc9、c00f9d92c427 或 7b00e91cd2fe 的,就是官方公告的受感染映像,用過它跑過的容器都要當成已中招。乾淨的 v1.106.2 是 7294b90f868c 開頭。Kubernetes 環境另外看節點上的映像快取。
  3. 翻 lockfile 與部署設定,看有沒有 1.107.0 或沒固定版本的 LiteLLM(唯讀)
    grep -rnE 'litellm' --include='requirements*.txt' --include='*.lock' --include='pyproject.toml' --include='Dockerfile*' --include='*compose*.y*ml' . 2>/dev/null
    看到什麼代表什麼:出現 1.107.0 要立刻處理;出現沒有版本號的 litellm、litellm/litellm:latest、main-stable、main-latest,代表你一直在追浮動版本,這次沒中只是運氣。把結果列成一份清單,改成固定版本與 digest。
  4. 查 21 日之後,雲端執行角色有沒有被拿去查 Bedrock(唯讀)
    aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=ListFoundationModels --start-time 2026-10-21T11:00:00Z --max-results 50 --query 'Events[].[EventTime,Username,EventName]' --output table
    看到什麼代表什麼:沒有輸出或只有你認得的使用者最好。出現執行角色、而你的應用程式根本不用 Bedrock,就去看那一筆的來源 IP 是不是你的基礎設施;Wiz 記錄的順序是先 GetCallerIdentity、再 ListFoundationModels。要長期盯,就把它做成一條 CloudTrail 告警,命名為 alarm-bedrock-enum-k7,和帳單告警放在同一個頻道。
  5. 看你的 LiteLLM 閘道是不是對公網開著(唯讀)
    curl -s -o /dev/null -w '%{http_code}' https://你的閘道網址/health/liveliness
    看到什麼代表什麼:從公司網路外面打得到、回 200,代表它對公網開放。這次的入口就是一套對外開放的內部 LiteLLM。內部用的閘道應該只在內網或 VPN 後面;一定要對外的,至少要有 IP 允許清單,並檢查使用者清單裡有沒有不認得的帳號。
  6. 列出你自己 GitHub 帳號上授權過的 OAuth 應用與權杖(唯讀)
    gh auth status
    看到什麼代表什麼:看 Token scopes 那一行:有 workflow、admin:org 這類權限的權杖,要問自己日常是否真的需要。再到 GitHub 設定頁的 Applications 看授權過的 OAuth 應用;組織擁有者的帳號,最好不要拿來在裝了 AI 代理的機器上日常開發。

回到那個保險櫃

保險櫃廠商寄來的新鎖頭,經過了廠商自己的品管、蓋了廠商的章,只是做鎖頭的模具被人換掉了。換上它的人沒有做錯什麼,他們信任的流程也都還在,只是每一道流程確認的都是「是不是廠商的人在操作」,而廠商的幾把員工識別證,早就躺在一本沒鎖的登記簿裡。

這篇能證明的有三件事:哪些版本有毒、它是從仿冒儲存庫建出來的、它會直接去撈閘道裡的上游金鑰。入口與損失還在等官方。但不用等就能做的事很明確:先確認自己有沒有中,中了就把能換的全部換掉;沒中的,把浮動版本改成固定版本,把閘道從公網上拿下來。

來源

分享
精選拆解直送信箱

每篇新文章一封:三點摘要、關鍵字定義、今天能做的一件事。

延伸閱讀

新聞稿拆解

主金鑰洩漏 155 天後:攻擊者劫持發布流程推毒版套件

LiteLLM 於 2026 年 5 月 27 日將主金鑰意外提交到公開的 GitHub 儲存庫,commit 雖然立即被 force-push 覆蓋,但主金鑰未被撤銷。攻擊者在 155 天後使用這個金鑰取得 Agent Platform 的存取權,竊取 GitHub CLI OAuth token,進而取得組織擁有者級別的存取權,修改發布流程指向偽裝儲存庫,於 10 月 21 日推送惡意版本 1.107.0 到 PyPI 與 Docker Hub。這個惡意套件會在 Python 啟動時自動執行,竊取開發者工具、AI 應用、雲端環境與瀏覽器的憑證,並每小時從攻擊者控制的伺服器取得並執行任意程式碼。

  • 2026/10/24
  • 每日拆解
研究報告拆解

Polymarket 遭劫持的 DebugBear 腳本攻擊:透過第三方 RUM 工具竊取 270 萬美元

2026 年 10 月 29 日,預測市場平台 Polymarket 載入的第三方效能監控腳本 DebugBear 遭 DNS 劫持,攻擊者將惡意程式碼注入已登入交易者的瀏覽器,在一小時內竊取約 270 萬美元加密貨幣。攻擊者沒有竊取私鑰或種子詞,而是重複使用已登入交易者在瀏覽器中的 API 金鑰與錢包簽署器,讓受害者自己的錢包簽署惡意交易。Polymarket 已退款給 726 個受影響錢包。

  • 2026/11/02
  • 每日拆解
事件拆解

Cursor 市集的官方 Claude Code 擴充套件被植入 SSH 後門:最多 5000 名使用者受影響

Cursor 市集上的 Claude Code 擴充套件(由 Anthropic 官方發布)在 2026 年 10 月 29 日遭到入侵,攻擊者以官方身分發布 2.1.313 惡意版本,試圖在受影響系統上安裝持久的 SSH 後門。惡意擴充套件會新增攻擊者控制的 SSH 金鑰、建立反向隧道至 134.209.82.225、安裝持久化機制,並回傳主機偵察資料。Cursor 已移除惡意版本並暫時關閉市集,Anthropic 發布 2.1.314 版本。

  • 2026/10/30
  • 每日拆解