LiteLLM 1.107.0 版 PyPI 供應鏈被植入 Credential Stealer 與 Backdoor
Compromised LiteLLM 1.107.0 on PyPI Installs a Credential Stealer and Backdoor
LiteLLM 1.107.0 版於 2026 年 10 月 21 日被發現在 PyPI 上安裝 Credential Stealer 與 Remote Code Execution Backdoor。
此報告揭示 PyPI 供應鏈攻擊的細節,提醒開發者加強審核與監控,並提供實際排查步驟,促進安全文化提升並強化依賴管理流程。
• Oct 21, 2026 • 6 分鐘閱讀
The LiteLLM 1.107.0 release on PyPI carries a credential stealer and a
remote code execution backdoor. All seven wheels (Linux, macOS, Windows) install them as three files in
site-packages. Both run when any Python process starts on the host, even one that never imports litellm. The
upstream GitHub source does not contain them. PyPI has since removed the release.
Versions 1.106.2, 1.107.0rc1, and 1.107.0rc2 are clean. The companion packages that 1.107.0 pins,
litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78, are also clean.
What the attack does
Installing litellm==1.107.0 places three files in site-packages. Python loads two of them each time it starts.
cook.pthimportscook.py, a credential stealer. It collects SSH keys, cloud credentials, CI/CD secrets, AI tool tokens, browser passwords, and the LLM provider keys in a LiteLLM proxy database. It encrypts the data and sends it towww.litellm[.]dev.sitecustomize.pystarts a background daemon. Every hour, the daemon reads a server address from a GitHub file, runs the stealer again, and downloads and runs code from that server.
How the implant got into the release
The pyproject.toml in the 1.107.0 source distribution (sdist) has one extra line in the maturin build section:
pyproject.toml (litellm-1.107.0.tar.gz)
TOML 6 lines
The maturin data = "cook" setting copies a cook/ folder into litellm-1.107.0.data/purelib/ in the wheel. pip
installs that folder into site-packages.
The sdist leaves out the cook/ folder, so this line is the only visible change. No public commit in
BerriAI/litellm contains data = "cook" or a cook/ folder.
The wheel RECORD lists the three added files:
litellm-1.107.0.dist-info/RECORD
TEXT 4 lines
The release does not match its tag
The tag v1.107.0 points to commit
66c8f5b (06:59 UTC). Its
pyproject.toml says version = "1.108.0". SafeDep compared every sdist file with the git trees by blob hash. The sdist
matches a later main commit,
a8113b0 (08:50 UTC), plus five
edited files and the cook/ folder.
Four of the edited files are Cargo.toml files that gain a readme = "README.md" line. The legitimate 1.106.2 and rc2
releases have the same edit. The fifth is pyproject.toml, compared here with a8113b0:
pyproject.toml (a8113b0 → litellm-1.107.0.tar.gz)
DIFF 11 lines
The edit sets the version back to 1.107.0 and pins the companion packages to the rc2 values. As a result, a dependency diff against rc2 shows nothing unusual. The legitimate 1.106.2 sdist matches its tag commit
49943cd except for the
Cargo.toml readme lines.
How the wheels were built
The malicious wheels match the output of the normal BerriAI release pipeline:
- The
WHEELfile namesmaturin1.9.4, the same version as in the legitimate releases. The implant files have correctRECORDentries, which shows no edits after the build. - Zip timestamps show separate native builds: macOS at 11:20, Linux at 11:21, and Windows at 11:37 UTC.
- In the Windows wheel, all 2,804
.pyfiles have CRLF line endings, including the three implant files. The Linux wheel has none. A git checkout on Windows with line-ending conversion produces this pattern.
No public GitHub Actions run built or published the release. SafeDep checked all 539 runs between 09:30 and 12:30 UTC.
At 11:50 UTC, about 10 minutes after the PyPI upload, litellm-dispatcher[bot] published the GitHub release
v1.107.0. The same delay occurred for 1.106.2. PyPI has no provenance attestations for 1.107.0, 1.106.2, or rc2.
SafeDep assesses, with moderate to high confidence, that BerriAI’s private release pipeline built these wheels from an attacker-modified git source. The attacker could have used a compromised maintainer or bot credential, a private branch or mirror, or the reference the pipeline builds from. A stolen PyPI token used from the attacker’s own machines fits the evidence less well. Only BerriAI’s pipeline logs can confirm the cause.
Payload 1:cook.py 中的憑證竊取程式
啟動時,Python 會在 site-packages 中執行每一行以 import 開頭的 .pth 檔案。cook.pth 包含
import cook,因此該竊取程式會在主機上的每個 Python 程式中執行:應用程式、pip、IDE 語言伺服器,以及 CI 任務。
cook.py 將約 61 KB 的 Python 程式碼包裝在一層 exec(zlib.decompress(base64.b64decode('...'))) 中。解碼後的
程式碼以此設定開頭:
cook.py (decoded)
PYTHON 10 行
它收集了什麼
| 模組 | 目標 |
|---|---|
sysinfo, netinfo | 主機名稱、使用者、作業系統、IP 位址、網路設定 |
browser_pw | Chromium 儲存的密碼,使用使用者設定檔主金鑰解密(Windows 上的 DPAPI)。Firefox logins.json 與 key4.db |
browser_hist | 瀏覽器歷史紀錄 |
ssh_keys | ~/.ssh/* |
shell_hist | bash 與 zsh 歷史紀錄、PowerShell PSReadLine、cmd 歷史紀錄 |
cloud_creds | ~/.aws/credentials 與 config、Azure 個人資料與憑證、gcloud 憑證、~/.kube/config、~/.docker/config.json、gh hosts.yml、rclone.conf、~/.netrc |
dev_secrets | .pem、.key、.pfx、.p12、.pk8、.ppk 檔案於 $HOME,Composer auth.json、FileZilla 儲存的伺服器 |
container_creds | Kubernetes 服務帳號憑證 |
cicd_secrets | GitLab runner config.toml、Jenkins initialAdminPassword 與 master.key、.vault-token、Terraform credentials.tfrc.json、Packer 設定 |
ai_devtools | Claude Code、Claude Desktop、Cursor、Windsurf、Copilot、VS Code mcp.json、Gemini CLI、Codex auth.json、Continue、Aider、Goose、opencode、Qwen、Crush、Kiro、Zed、Hugging Face token |
env_vars | 自己的環境及其他程序的環境,透過/proc/<pid>/environ |
db_litellm | 讀取DATABASE_URL並轉存LiteLLM_CredentialsTable與LiteLLM_ProxyModelTable表格,該表格儲存 LiteLLM 代理的 LLM 供應商 API 金鑰 |
cloud_metadata | AWS 例項元資料服務(IMDSv2,後續為 v1):例項身份與角色憑證 |
gpg_keys、tls_privkey | GPG 金鑰、TLS 私鑰 |
資料傳送方式
- 它以隨機 AES-256-CBC 金鑰加密 JSON 結果,並以 RSA-OAEP 與嵌入的公開金鑰加密該金鑰。
- 它將結果打包為 tar.gz(
hdr.json、data.bin、key.bin),並以 POST 請求傳送至www.litellm[.]dev/healthcheck,標頭為X-Token: XWiA8gZNa7NcX2XRoAEb。 - 上傳成功後,它寫入
/tmp/.pc_once_72129118c59f5636c0bb,確保每臺主機僅收集一次。它亦設定PRIVCOLLECT_ACTIVE=1以避免同一程序樹中再次執行。
Payload 2:位於 sitecustomize.py 的後門
Python 在啟動時匯入sitecustomize,因此此檔案亦會在每個 Python 程序中執行。
sitecustomize.py
PYTHON 5 行
- 它在
/tmp/remote_code_loader.lock上取得排他鎖,確保每臺主機只執行一次守護程式。 - 它呼叫
fork()和setsid(),將標準輸入與輸出送至/dev/null,並切換工作目錄至/以從父程序分離。 - Every hour, it:
- 從攻擊者的 GitHub 倉庫讀取
domain.txt,並取第一行看似域名的內容, - 執行其內嵌的竊取程式副本,該程式沒有一次性主機標記,
- 下載
http://<domain>/codepy並以exec()執行。
- 從攻擊者的 GitHub 倉庫讀取
攻擊者可藉由編輯domain.txt將守護程式指向新伺服器。每臺感染主機會執行該伺服器回傳的codepy。
於 13:40 UTC,GitHub 帳號letters9813及其倉庫回傳 404,距釋出約兩小時。後門現在找不到伺服器位址,並不執行任何操作。已在刪除前執行守護程式的主機可能已接收並執行第二階段程式碼。SafeDep 無法找回伺服器位址。
時間表
所有時間皆為 2026-10-21 UTC。
| 時間 | 事件 |
|---|---|
| 06:59 | 標籤v1.107.0提交66c8f5b已建立 |
| 08:50 | 提交a8113b0於main,為釋出原始碼基礎 |
| 10:44 | 合法版 1.106.2 已上傳至 PyPI |
| 11:20 至 11:37 | 已建立惡意 1.107.0 wheels |
| 11:39 至 11:40 | 1.107.0 已上傳至 PyPI(全部 8 個檔案) |
| 11:50 | litellm-dispatcher[bot] 發布 GitHub 釋出 v1.107.0 |
| 13:40 | 觀測到:1.107.0 已從 PyPI 消失,letters9813 帳號已刪除 |
妥協指標
網路
| 指標 | 目的 |
|---|---|
www.litellm[.]dev, hxxp://www.litellm[.]dev/healthcheck | 接收被盜資料 |
HTTP 標頭 X-Token: XWiA8gZNa7NcX2XRoAEb | 每次上傳時送出 |
raw.githubusercontent[.]com/letters9813/pythonclient-test/main/domain.txt | 後門在此讀取其伺服器位址(帳號已刪除) |
hxxp://<domain>/codepy | 第二階段程式碼下載 |
Python 程式請求 169.254.169.254 | 盜取 AWS 例項憑證 |
主機
| 指標 | 意義 |
|---|---|
cook.pth, cook.py, sitecustomize.py 在 site-packages | 植入已安裝 |
/tmp/.pc_once_72129118c59f5636c0bb | Stealer 已上傳資料 |
/tmp/remote_code_loader.lock | 後門守護程式已執行 |
環境變數 PRIVCOLLECT_ACTIVE=1 | 存在於受感染程式中 |
分離的 Python 守護程式(父 PID 1,獨立會話,工作目錄 /) | 後門迴圈 |
檔案雜湊值(SHA-256)
| 檔案 | SHA-256 |
|---|---|
litellm-1.107.0.tar.gz | 2befe2a79fb4fbbd9a543b5b40b530c8cffa8771bab22b8fb22b2e4ae3b1f819 |
litellm-1.107.0-cp310-abi3-manylinux_2_28_x86_64.whl | 462d0c9f67ca522d9e0e4acd245e90b36ce0d5353604a66bb7c43a1ec4a6fcf7 |
litellm-1.107.0-cp310-abi3-manylinux_2_28_aarch64.whl | 668169d35b87bf51324080390b1b1c5ab12bd335cfe5f1a99e3689abd5e02ae6 |
litellm-1.107.0-cp310-abi3-musllinux_1_2_x86_64.whl | 1bbce8caa94f01c0aaa0b7ddae51553a8a32e1ce53d96d44fe583272aff7ad00 |
litellm-1.107.0-cp310-abi3-musllinux_1_2_aarch64.whl | c2e32d0ce2548aa662c18bf5001911a79fe5b30cf8357132275add02fdcde054 |
litellm-1.107.0-cp310-abi3-macosx_10_12_x86_64.whl | 7598727f209d717041db37f7dca271d3a7d328a279890e0e1d55375729aeb86d |
litellm-1.107.0-cp310-abi3-macosx_11_0_arm64.whl | 0aaf7ba346d41ef2f40390436738b001180d5a1c03aa093f121078a9969d9a1a |
litellm-1.107.0-cp310-abi3-win_amd64.whl | 12d4ccc5c6f8d35cf4f2cf233b7f3e418e863e657d81ff5f5fc9e210005b9484 |
植入的檔案,來自 wheel RECORD:
| 檔案 | SHA-256 (URL 安全 base64) |
|---|---|
cook.pth | fxkWMucyBwXRVLhSBPGuVGtUOsvX6IYSKbdvUEK5K9s |
cook.py | ndCjkm-Wo364bPHuIxDZvpMTnMTr7z754u_pUxOQviU |
sitecustomize.py | NL7wT-iKydCq4EdWMLFzhoHRW5TyM6yKpG6s9ZaDBfo |
- pypi
- python
- malware
- supply-chain
- litellm
作者
![]()
最新來自 SafeDep blogs
追蹤以獲得最新的開源安全 & 工程更新與洞察
傳送程式碼。
不是惡意軟體。
在您的機器上免費使用開源工具,並擴充套件到組織的統一平臺。