Malicious LiteLLM 1.107.0 Steals Credentials and Backdoors Hosts
A threat actor hijacked LiteLLM's release pipeline with a leaked, never-revoked master key and pushed a malicious 1.107.0 to PyPI and Docker on October 21, 2026. The wheel installs Python startup files that steal credentials and open an hourly backdoor even when LiteLLM is never imported. PyPI quarantined the version after 1 hour 44 minutes; anyone who installed it must rebuild the environment and rotate every reachable secret.
TL;DR
- LiteLLM 1.107.0, published to PyPI at 11:39 UTC on October 21, 2026 and to Docker Hub and GHCR at 11:46 UTC, was built by an attacker, not the project. PyPI quarantined it at 13:23 UTC and the Docker tags were removed or repointed at 14:20 UTC, after Upwind reported the attack at 12:06 UTC.
- The malicious wheel writes Python startup files outside the LiteLLM package directory, so the payload runs every time Python starts in that environment, even when LiteLLM is never imported. Removing only the package leaves the startup components behind, as Microsoft's security researchers documented.
- The payload is a credential stealer that collects SSH and Git credentials, environment variables, registry and package-manager tokens, cloud and AI keys and browser data and posts it to litellm[.]dev/healthcheck, plus a Unix backdoor that fetches and runs attacker-supplied code every hour.
- If anything in your estate installed or pulled 1.107.0 during the window, treat the host and its secrets as compromised: rebuild the environment from a verified clean version, and rotate every credential the host, the build and the surrounding CI could reach. The last clean PyPI release is 1.106.3.
Am I affected?
You are affected if all of these hold:
- Something installed litellm version 1.107.0 from PyPI between 11:39 UTC and the quarantine at 13:23 UTC on October 21, 2026, or pulled a LiteLLM Docker or GHCR image published from 11:46 UTC until the tags v1.107.0, latest, main-latest and main-stable were removed or repointed at 14:20 UTC.
- The environment was started at least once after the install. Execution happens through Python startup files, so importing LiteLLM is not required and never having called it does not protect the host.
- You have not since rebuilt the environment and rotated its secrets.
Check what is installed and pinned, on hosts, in CI runners and in images:
pip show litellm | grep -E '^(Name|Version|Location)'
grep -rn 'litellm' requirements.txt pyproject.toml 2>/dev/null
grep -n -A2 'name = "litellm"' uv.lock poetry.lock 2>/dev/null # shows the pinned version
docker images | grep -i litellm # local caches
In CI and developer machines, also look for Python startup files that should not be there: unexpected .pth files, sitecustomize.py or usercustomize.py in the environment's site-packages or user-site directories. That is the persistence mechanism the wheel used. As of October 25, PyPI lists no final 1.107.0 release at all (only pre-release dev and rc builds) and 1.106.3 is the newest final version, which is itself a quick confirmation that a final 1.107.0 was never legitimately published.
For scale: litellm logged 21,343,835 PyPI downloads in the last week (pypistats, checked October 25, 2026), so a 1 hour 44 minute exposure window still reaches a large population of automated installs, CI caches and nightly builds.
What does the malicious code do?
The malicious release turns LiteLLM from a library into a host-level implant by installing code into Python's startup path, outside the package that a normal uninstall removes.
Analysis by Socket, by the OpenSourceMalware research group and by Microsoft Security Research agrees on two execution paths inside the 1.107.0 wheel:
- A credential harvester that runs at Python startup. It collects system information, SSH files, Git and GitLab credentials and tokens, environment variables, authentication material from developer tools, AI applications, cloud environments and web browsers, and tokens associated with npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems and Cargo. The data is compressed and sent in an HTTP POST to litellm[.]dev/healthcheck, a domain registered on October 13, 2026, eight days before the release.
- A Unix-oriented backdoor that launches as a background process, contacts the attacker and fetches and executes arbitrary code every hour, giving persistence that survives a stolen-session fix or a password change.
The decisive detail is where the code sits. The wheel packed three files, cook.pth, cook.py and sitecustomize.py, under its litellm-1.107.0.data/purelib/ data directory, which pip copies to the root of site-packages at install time, outside the litellm package; the files are listed with valid hashes in the wheel's RECORD manifest, so they were built into the release rather than added afterward. Python runs .pth files and sitecustomize.py from site-packages on every interpreter start. Microsoft Security Research states that the malicious startup files resided outside the LiteLLM application directory, so "simply starting Python in an affected environment could activate the malicious code, even when LiteLLM itself was never imported", and "removing only the application files could leave the malicious startup components behind". Uninstalling or upgrading the package is therefore not cleanup; rebuilding the environment is.
How did the compromise happen?
LiteLLM's own disclosure reconstructs the full chain, and Socket's timeline adds the workflow-level detail:
- On May 27, 2026, a configuration file committed to the public LiteLLM-Labs/lite-harness repository contained the master key to a LiteLLM Agent Platform deployment. The mistake was spotted, the commit was force-pushed over, and the key was marked as requiring revocation, but the revocation never happened.
- On October 10, an attacker operating from 69.48.229[.]143 used that unrevoked master key to enter the Agent Platform and extract a GitHub CLI OAuth token configured there. The token belonged to the GitHub organization owner's account and was not scoped down, giving owner-level access to every repository in the organization.
- Between 22:11 and 22:12 UTC that day, the attacker pushed commits to short-lived branches named ci-tmp-a351cc and ci-tmp-4d8517 on internal repositories (BerriAI/litellm and BerriAI/litellm-auto), each adding a workflow at
.github/workflows/cache.ymlwith the innocuous subject "ci: bump cache config", triggered on every push. The workflows harvested GitHub tokens and API keys for the organization's Linear workspace, the LiteLLM deployment and Together AI, plus a GitHub App identifier and signing key, and sent them to two attacker-controlled webhook[.]site endpoints. The runs and branches were deleted minutes later. A second round on October 12 extended the harvest to Anthropic, OpenAI and AWS Bedrock keys, a Tavily key, a GitGuardian secret-scanning tenant key, Playwright storage state, and PostgreSQL and Redis credentials. The GitGuardian tenant key matters defensively: it is a credential for the secret-scanning platform itself, visibility into, and potentially control over, the alerts that would have surfaced the activity. - On October 21 between 10:08 and 11:08 UTC, the attacker overrode branch protection and repointed the release pipeline's source from BerriAI/litellm to a lookalike repository, "Berri-AI/litellm", which inserts a hyphen between "Berri" and "AI". Releases normally require an authorized engineer plus approval from a special account locked to a physical device in the office; that restriction had been relaxed earlier in the week to any two engineers. The attacker used one stolen maintainer token to trigger the release and another to approve it.
- PyPI received 1.107.0 at 11:39 UTC, Docker Hub and GHCR images followed at 11:46, and the production and staging run records were deleted at 11:53. Upwind researcher Lidor Machluf reported the supply-chain attack at 12:06, PyPI quarantined the version at 13:23, and the Docker tags were removed or repointed to verified clean images at 14:20.
LiteLLM ruled out its initial hypothesis that the GitHub tokens had leaked through internal deployment logs, and is now revoking every token associated with every internal system and vendor platform.
Attack flow
public repo, never revoked (May 27)"] --> B["Attacker enters the Agent
Platform with the key (Oct 10)"] B --> C["Org-owner GitHub CLI
token extracted"] C --> D["ci-tmp-* branches run
token-harvest workflows"] D --> E["Release pipeline repointed to
lookalike Berri-AI repo (Oct 21)"] E --> F["Malicious 1.107.0 ships
to PyPI, Docker Hub and GHCR"] F --> G["Startup hooks steal credentials to
litellm[.]dev; backdoor runs hourly"] style E fill:#f8d7da
What can an attacker do?
On any host where the malicious wheel ran:
- Read and exfiltrate every credential the process owner can read: SSH keys, Git configuration and tokens, cloud provider and AI provider keys, database and cache credentials, environment variables, and package-registry tokens for npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems and Cargo.
- Re-execute attacker-supplied code hourly through the backdoor, so an attacker retains access after the stolen credentials are rotated unless the environment is rebuilt.
- Pivot through harvested CI and platform credentials: maintainer-level GitHub tokens, a GitHub App signing key, a GitGuardian scanning tenant key, and the same class of upstream API tokens LiteLLM proxies for its users.
For the project's maintainers, the impact reached further than one release: the attacker held owner-level GitHub organization access for eleven days, enough to read and modify every repository, and harvested secrets from two engineers with maintainer privileges.
What should I do if I installed it?
There is no version to upgrade into: a clean final 1.107.0 does not exist on PyPI. Treat an installation of 1.107.0 as a host compromise, in this order:
- Isolate the affected hosts, containers and CI runners. Stop them rather than logging into them for casual inspection, since starting Python re-executes the payload.
- Rebuild the environments from scratch (new container images, fresh virtual environments, new CI runners) on a verified clean version. Pin
litellm==1.106.3, the last clean release, or a later verified release when the project ships one. Do not onlypip uninstall litellm: the startup files sit outside the package directory and survive it. - Rotate every secret the environment, its build and its CI could reach: environment variables, SSH keys, Git and GitLab tokens, cloud and AI provider keys, database and Redis credentials, browser-stored credentials, and package-registry tokens for every ecosystem the stealer targets (npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems, Cargo). Assume the attacker holds them, not merely saw them.
- Check and clean the source of the install: CI caches, base images and Docker digests pulled between 11:39 and 14:20 UTC on October 21, and the lockfiles that may still pin 1.107.0 for the next build.
- If you are anywhere near the LiteLLM organization or its engineers' workflows, review the GitHub audit log for the ci-tmp-* branches, the deleted workflow runs, and any branch-protection changes, and rotate the GitGuardian tenant key.
How do I check if I was compromised?
Indicators from the public analyses, all published:
- Package presence:
pip show litellmreporting version 1.107.0, or lockfiles and image digests pinning it. The PyPI exposure window is 11:39 to 13:23 UTC on October 21, 2026; the Docker Hub and GHCR window is 11:46 to 14:20 UTC. - Network: HTTP POSTs to litellm[.]dev/healthcheck (domain registered October 13, 2026), and any connection to the two webhook[.]site exfiltration endpoints used by the workflow harvest, 65a42464-3cfa-4b02-a18c-17a6d7b86f37 and 4da03473-a3e7-4dc2-a087-9c564b4208e1, which apply to the project's CI as well.
- Host: the startup files
cook.pth,cook.pyandsitecustomize.pyin thesite-packagesdirectory of environments that installed the wheel, or any other unexpected.pth,sitecustomize.pyorusercustomize.py; the runtime markers/tmp/remote_code_loader.lockand/tmp/.pc_once_*; an unexplained background process contacting a remote host on an hourly schedule. - GitHub organization (maintainers): branches named ci-tmp- followed by hex, commits adding
.github/workflows/cache.ymlwith the subject "ci: bump cache config", workflow runs deleted minutes after creation on October 10 and 12, and branch-protection or pipeline changes on October 21.
Timeline
- 2026-05-27: the Agent Platform master key is committed to the public LiteLLM-Labs/lite-harness repository; the commit is force-pushed over but the key is never revoked.
- 2026-10-10: the attacker uses the key, extracts an organization-owner GitHub token, and runs the first token-harvesting workflow from a ci-tmp-* branch at 22:11 UTC.
- 2026-10-12: a second workflow widens the harvest to AI, cloud, database and secret-scanning credentials.
- 2026-10-13: the litellm[.]dev exfiltration domain is registered.
- 2026-10-21: at 10:08 UTC the release pipeline is repointed to the lookalike repository; 1.107.0 reaches PyPI at 11:39 and Docker at 11:46; run records are deleted at 11:53; Upwind reports the attack at 12:06; PyPI quarantines the version at 13:23; Docker tags are cleaned at 14:20.
- 2026-10-23: Microsoft Security Research publishes the Python-startup-hook analysis.
- 2026-10-24: LiteLLM publishes its root-cause disclosure; Socket publishes the workflow timeline.
- 2026-10-25: This post.
FAQ
Was PyPI itself compromised?
No. The attacker pushed through LiteLLM's own release pipeline using maintainer-level GitHub credentials, so from PyPI's perspective the upload looked legitimate until researchers reported it. PyPI quarantined the version one hour forty-four minutes after publication, which limited but did not eliminate the window.
Am I safe if I installed 1.107.0 but never imported LiteLLM?
No. The malicious code is installed as Python startup files outside the LiteLLM package directory, so it executes when the interpreter starts, before any import. Environments that merely had the package installed and ran unrelated Python code are as exposed as applications that use LiteLLM as their LLM gateway.
Can I just uninstall the package or upgrade past 1.107.0?
No. Uninstalling removes the package, not the startup files, and there is no clean 1.107.0 to upgrade into: PyPI shows 1.106.3 as the newest final release. Rebuild the environment and pin the clean version instead, then rotate credentials.
Are the Docker images as dangerous as the wheel?
Yes. The same malicious build was published to Docker Hub and GHCR at 11:46 UTC, and the affected tags were removed or repointed only at 14:20. Images pulled in that window, including via latest, carried the same payload, and cached digests can still serve them to builds that pin by digest.
Why did the release approval not stop this?
The approval normally comes from a special account locked to a physical device, but the restriction had been relaxed that week to speed up releases: any two engineers could trigger and approve. The attacker used one stolen maintainer token to trigger the release and another to approve it, staying inside the relaxed rule.
Does this affect LiteLLM's proxy virtual keys too?
Everything the process could read should be treated as harvested: the stealer collects environment variables and cloud and AI provider credentials, which is exactly where LiteLLM deployments hold their upstream keys and database configuration. Rotate them along with everything else rather than reasoning about which ones the code could reach.
The stealer is also built for LiteLLM proxy deployments. When DATABASE_URL is set, it starts a Python child process that uses Prisma to read the credential_values field of LiteLLM_CredentialsTable and the litellm_params field of LiteLLM_ProxyModelTable, up to 500 rows per query, which takes the upstream model credentials stored in the proxy database along with the environment.
Scan your repositories, container images and CI configuration with Ostorlab to surface every environment that pins litellm 1.107.0 before you rebuild it.
References
- LiteLLM's root-cause disclosure, "Security incident October 21, 2026: attack chain", by Oliver Jensen and Mateo Wang, October 24, 2026: https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain (checked October 25, 2026)
- The leaked configuration file in the public LiteLLM-Labs/lite-harness repository at commit e10b14a: https://github.com/LiteLLM-Labs/lite-harness/blob/e10b14a/harnesses/opencode/opencode.json (checked October 25, 2026)
- LiteLLM maintainer's correction of the initial token-leak hypothesis: https://github.com/BerriAI/litellm/issues/47826#issuecomment-6374891275 (checked October 25, 2026)
- Microsoft Security Research, "Inside the LiteLLM 1.107.0 supply chain compromise: credential theft through Python startup hooks", October 23, 2026: https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/ (checked October 25, 2026)
- Socket's investigation of the malicious ci-tmp-* workflow commits and their exfiltration endpoints: https://socket.dev/blog/litellm-attack-timeline (checked October 25, 2026)
- OpenSourceMalware's analysis of the wheel's two execution paths: https://opensourcemalware.com/blog/litellm-1-107-0-pypi-wheel-contains-a-credential-stealer-and-persistent-python-backdoor (checked October 25, 2026)
- The Hacker News on the leaked master key and pipeline hijack, October 24, 2026, quoting the LiteLLM disclosure: https://thehackernews.com/2026/10/leaked-litellm-master-key-let-attackers.html (checked October 25, 2026)
- The Hacker News on the initial report of the compromised package by SafeDep, Socket, StepSecurity and Upwind, October 21, 2026: https://thehackernews.com/2026/10/litellm-compromised-in-supply-chain.html (checked October 25, 2026)
- PyPI release history for litellm, showing no final 1.107.0 and 1.106.3 as the newest final release: https://pypi.org/pypi/litellm/json (checked October 25, 2026)
- PyPI download statistics for litellm, 21,343,835 downloads in the last week: https://pypistats.org/packages/litellm (checked October 25, 2026)