LiteLLM PyPI Package and Docker Images Compromised to Steal Cloud and AI Credentials A supply chain attack compromised LiteLLM version 1.107.0 on PyPI and Docker, delivering a credential stealer that collects cloud credentials, private keys, browser data and AI provider API keys, according to Socket, whose AI scanner flagged the seven distributed wheels seven minutes after publication. Upwind's public investigation found the same payload in a published LiteLLM image, and the image's build provenance records a source repository named Berri-AI/litellm — differing by one hyphen from the legitimate BerriAI/litellm — with the malicious build running from 10:51:23 to 11:06:31 UTC on October 21, 2026. LiteLLM, a common interface and proxy for large language model providers with roughly 23M weekly downloads, activates the payload via Python startup hooks (cook.pth, cook.py and sitecustomize.py) without the application importing LiteLLM, so teams running 1.107.0 should stop deploying it and investigate proxy containers and CI runners where Python ran after installation. Socket’s AI Scanner flagging the malicious version of package. https://socket.dev/pypi/package/litellm/overview/1.107.0/cp310-abi3-macosx-10-12-x86-64-whl litellm A supply chain attack compromised LiteLLM 1.107.0 on PyPI and Docker, spreading malware that steals cloud credentials, developer secrets, and AI provider keys. LiteLLM 1.107.0 https://socket.dev/pypi/package/litellm/overview/1.107.0/cp310-abi3-macosx-10-12-x86-64-whl delivers a credential stealer through Python startup hooks, while container build provenance points to a lookalike source repository used by the project’s release workflow. LiteLLM version 1.107.0 contains malicious Python startup files that collect cloud credentials, private keys, browser data, and authentication files for AI development tools. Socket’s AI scanner flagged the seven distributed wheels seven minutes after publication, and static analysis confirms both a credential stealer and a loader capable of executing remotely supplied Python. Once the files are installed, a normal Python startup can activate them without the application importing LiteLLM. The incident also reaches container users. Upwind’s public investigation https://github.com/BerriAI/litellm/issues/47826 issuecomment-6316601885 reports the same payload in a published LiteLLM image. Examination of the image’s attached build provenance confirms that the release workflow recorded a source repository named Berri-AI/litellm , differing by one hyphen from the legitimate BerriAI/litellm project. Socket’s AI Scanner flagging the malicious version of litellm https://socket.dev/pypi/package/litellm/overview/1.107.0/cp310-abi3-macosx-10-12-x86-64-whl package. LiteLLM provides a common interface and proxy for large language model providers. It receives approximately 23M weekly downloads. A compromised installation can therefore run alongside provider API keys, cloud identities, and database connection strings. Teams using 1.107.0 should stop deploying it and investigate environments where Python ran after installation, including proxy containers and CI runners. The strongest evidence for the delivery path is in the container registry. The provenance attached to the Linux amd64 image identifies the private BerriAI/project-releaser workflow, run 39587059944 , and source revision 20535d8fa11f786a6c24df7488a0e449ed5f09ad . Its recorded dispatch inputs explicitly select the hyphenated source owner: { "git ref": "20535d8fa11f786a6c24df7488a0e449ed5f09ad", "release type": "stable", "repo": "litellm", "source owner": "Berri-AI" } The same document records vcs:source as https://github.com/Berri-AI/litellm . By comparison, the provenance for v1.106.2 records the legitimate BerriAI/litellm source. The malicious build started at 10:51:23 UTC on October 21, 2026, and finished at 11:06:31 UTC. These values come from the registry’s attached provenance documents, independently retrieved during this investigation. The referenced revision contains cook/purelib/cook.pth , cook/purelib/cook.py , and cook/purelib/sitecustomize.py . All three files match the inspected PyPI wheel byte for byte. This connects the source revision recorded in the container build to the malware delivered in that wheel. A commit at 09:48:21 UTC https://github.com/BerriAI/litellm/commit/783c43bc455d2e05d3cfaf194c2f325cfeab1433 adds one setting to the Maturin build configuration: tool.maturin manifest-path = "litellm-rust/crates/python-bridge/Cargo.toml" module-name = "litellm.rust bridge. native" python-source = "." +data = "cook" python-packages = "litellm cli" That setting supplies the data directory containing the startup payloads. A later commit at 10:46:29 UTC https://github.com/BerriAI/litellm/commit/20535d8fa11f786a6c24df7488a0e449ed5f09ad adds COPY cook ./cook to the backend, gateway, and migrations Dockerfiles, making the malicious directory available in their build stages. The tagged release’s pyproject.toml lacks the added data setting. Reviewing the tag alone would therefore miss this packaging change. The available evidence establishes what source the container build recorded and how the payload was included. It does not establish how the source-owner override was authorized, whether an account or token was compromised, or whether that same workflow uploaded the PyPI wheels. The public run URL returned 404; its private job logs were not available. The provenance is useful evidence of recorded build inputs, not proof that those inputs were trustworthy or an independently verified signature on every claim it contains. Upwind’s original report https://github.com/BerriAI/litellm/issues/47826 also identifies all seven 1.107.0 wheels across macOS, Linux, and Windows as affected. It also reports a modified source distribution whose build configuration contains data = "cook" , although that archive does not contain the three payload files. Socket’s saved artifact response records publication at 11:39:51 UTC and malware alerts at 11:46:30 UTC on October 21. At approximately 13:14 UTC, independent registry checks found that ghcr.io/berriai/litellm tags v1.107.0 , latest , main-latest , and main-stable all resolved to the same image index. Deployments following a floating tag could therefore receive the affected release without explicitly selecting 1.107.0 . Upwind additionally reports affected litellm-database and litellm-non root images on GHCR and litellm/litellm on Docker Hub. Its update distinguishes direct file verification in the main amd64 image from the other builds sharing the malicious source revision. Those additional image payloads were not independently extracted for this draft. The wheels carry entry points outside the main litellm/ directory: litellm-1.107.0.data/purelib/cook.pth litellm-1.107.0.data/purelib/cook.py litellm-1.107.0.data/purelib/sitecustomize.py The wheel installation format https://packaging.python.org/en/latest/specifications/binary-distribution-format/ places these files in the environment’s Python library directory. During normal startup, Python processes executable .pth lines and then attempts to import sitecustomize , as documented in its site initialization behavior https://docs.python.org/3.13/library/site.html . The complete contents of cook.pth are just: python import cook That line launches the credential collector. sitecustomize.py provides a second startup entry point for the background loader. The trigger matters operationally: starting Python or pip in an affected environment to inspect or repair the installation can execute the payload again. Downloading the wheel or inspecting its ZIP contents as data does not execute these hooks. Both Python files embed Base64-encoded, zlib-compressed source. Static decoding recovered nearly identical collectors, approximately 61 KB each. The cook.py wrapper starts its collector in a daemon thread and sets PRIVCOLLECT ACTIVE=1 to suppress duplicate collection in the same inherited environment. After an upload receives HTTP 200, it attempts to create /tmp/.pc once 72129118c59f5636c0bb , which suppresses subsequent collection for that variant. A short-lived interpreter can exit before the collection thread finishes. Conversely, an absent marker does not clear a host: failed uploads, temporary-directory cleanup, or container replacement can all leave no marker behind. The decoded payload, which uses the identifier privcollect , enables 16 collection modules. Each module runs behind its own error handler, allowing collection to continue when another target is unavailable. The targets span several credential boundaries: .netrc , .env files, SSH keys, GPG private keys, and TLS private keys. ~/.codex/auth.json , Claude’s ~/.claude/.credentials.json , Gemini’s ~/.gemini/oauth creds.json , and Cursor’s ~/.cursor/mcp.json , alongside configuration for Copilot, Windsurf, Kiro, Zed, Continue, OpenCode, and Hugging Face. GetCallerIdentity . One module specifically targets LiteLLM deployments with DATABASE URL configured. Its Prisma child program attempts these queries: SELECT "credential name", "credential values" FROM "LiteLLM CredentialsTable" LIMIT 500; SELECT "model name", "litellm params" FROM "LiteLLM ProxyModelTable" LIMIT 500; This is explicit targeting of the proxy’s saved credentials and model configuration. It does not demonstrate successful access to a particular deployment or decryption of every returned credential value. The inspected macOS artifact also has platform limits. Its database module attempts Linux /proc inspection before launching the child, which fails on ordinary macOS installations. Its Chromium password routine does not recover plaintext v10/v11 passwords on macOS, although it still copies sensitive browser databases. Other modules, including AI-tool files and the current process’s environment, remain applicable. The configuration value PC TARGET='linux' is an output label, not a check that restricts execution to Linux. The collector encrypts its JSON results with a randomly generated AES-256-CBC key, then wraps that key with an embedded RSA-4096 public key using OAEP. It packages the results into a gzip-compressed tar archive containing hdr.json , data.bin , and key.bin . The archive is POSTed to hxxp://www . litellm . dev/healthcheck . Despite the URL’s health-check wording, the request body contains the encrypted collection results. The request includes an X-SHA256 body checksum, Content-Type: application/octet-stream , and the fixed header X-Token: XWiA8gZNa7NcX2XRoAEb . The implementation makes up to four attempts, with a 120-second timeout per request. This endpoint is an exfiltration destination. The collector checks the response status and does not execute the response body. Remote code delivery uses a separate path. sitecustomize.py obtains a lock on /tmp/remote code loader.lock , forks a detached process, redirects its standard streams to /dev/null , and changes its working directory to / . The process repeatedly retrieves domain.txt from the letters9813/pythonclient-test GitHub repository. When the file supplies a suitable domain that resolves, the loader reaches its embedded collector and downloads Python from http://