How TeamPCP Poisoned Six Python Packages and Breached Over 1000 Organizations in Five Weeks

Want to learn ethical hacking? I built a complete course. Have a look!
Learn penetration testing, web exploitation, network security, and the hacker mindset:
→ Master ethical hacking hands-on
Hacking is not a hobby but a way of life!
A group of attackers has been quietly poisoning Python packages for five weeks straight. They have exfiltrated data from over 500,000 infected machines, hit more than 1,000 organizations, and confirmed victims include Aqua Security, Checkmarx, and government infrastructure including the European Commission’s AWS environment. Yesterday they struck again. This time the target was Xinference, an open-source framework used by developers to run AI models locally. Versions 2.6.0, 2.6.1, and 2.6.2 were compromised and have since been pulled from PyPI. If you installed or updated Xinference in the last 24 hours without pinning your version, you need to act now.
But Xinference is not the real story here. It is the latest chapter in something much bigger.
To understand what is actually going on, you have to go back to February 27, when an attacker exploited a Pwn Request vulnerability inside Aqua Security’s Trivy CI/CD pipeline. Trivy is one of the most widely used open-source vulnerability scanners in the world, the kind of tool that runs inside thousands of automated build pipelines to check for security issues. They abused GitHub Actions inside Trivy’s own pipeline to steal a personal access token from an automated bot account. Aqua Security changed their credentials afterward, but not all at once. There was a window of a few days where old tokens still worked, and during that window the attackers grabbed access they would hold onto for nearly three weeks before striking.
On March 19, the attacker used that leftover access to push a malicious version tag to the Trivy GitHub repository. This attack is tracked as CVE-2026-33634, CVSS 9.4. Every pipeline that pulled Trivy during that window executed the compromised version, which immediately harvested whatever credentials it could find in the CI/CD environment and sent them home. Those credentials included PyPI publishing tokens, SSH keys, cloud access keys, and API tokens for services those pipelines were connected to.
What followed was a chain reaction that researchers are still fully mapping. The reason so many tools show up in this story is that it was never a list of separate attacks. It was one attack that kept opening the next door. They compromised Trivy, and Trivy ran inside LiteLLM’s build pipeline. That gave them LiteLLM’s PyPI publishing token. With that token they pushed a poisoned version of LiteLLM. That version ran on developer machines and stole their credentials. Every step handed them the key to the next one.
- → March 20: stolen npm tokens were used to push malicious versions of 66+ npm packages, with a self-propagating worm component that used the infected developer’s own publishing credentials to spread to packages they maintained.
- → March 22: 44 Aqua Security repositories were defaced and malicious Docker images were published. Researchers also found a wiper component inside the payload apparently designed to target systems in Iran, though the full picture there remains unclear.
- → March 23: Checkmarx KICS GitHub Actions were hijacked and malicious VS Code extensions were published to the OpenVSX marketplace.
- → March 24: LiteLLM, a Python library that routes API calls across over 100 different AI providers and sees roughly 95 million downloads per month, was poisoned on PyPI.
- → March 27: the Telnyx Python SDK was compromised using a delivery method that had not been seen before in a PyPI supply chain attack, with the second-stage payload embedded inside WAV audio files using a steganographic scheme. The first eight bytes of the decoded blob serve as the XOR key, and the remaining bytes form the payload, decrypted byte-by-byte against that rotating key. This is the exact decode logic researchers found inside the package:
| |
To anything watching the network, it looked like the application downloaded a ringtone.
And then yesterday: Xinference.
The group behind this campaign goes by the name TeamPCP, also tracked under the aliases PCPcat, ShellForce, and DeadCatx3. In cybersecurity, attribution is one of the hardest problems you will ever try to solve. IP addresses can be spoofed. Tools get shared. Markers can be planted. What we can say with confidence is that the malware in each of these attacks contains the string TeamPCP Cloud stealer and the comment # hacked by teampcp inside the decoded payload, and that the same payload structure, the same credential targeting logic, and the same operational pattern shows up across Trivy, LiteLLM, Telnyx, and now Xinference. Multiple research teams all point to the same group, even though TeamPCP publicly denied being behind the Xinference attack, saying someone copied their tools and slapped their name on it. What makes this more serious than a typical credential theft operation is that TeamPCP works together with a ransomware group called Vect. TeamPCP handles the break-in and the credential theft. Vect handles the extortion. The stolen keys do not just sit in a database somewhere. One more thing worth mentioning: in a Forbes interview, a TeamPCP spokesperson claimed the group used Anthropic’s Claude to help build payload components, not to find vulnerabilities, but to speed up malware development. That claim has not been independently verified by researchers, and groups like this sometimes make statements for effect. But if true, it puts this campaign in a broader conversation about what AI-assisted offensive tooling actually looks like in the wild.
There are technical details in the Xinference payload that do support the copycat theory. The previous attacks encrypted stolen data with AES-256-CBC combined with an RSA-4096 public key before exfiltrating it. That RSA key showed up in every confirmed TeamPCP attack before this one, and it is how researchers fingerprint their work. The Xinference payload does not have it. The exfiltrated archive is named love.tar.gz, not tpcp.tar.gz like every previous attack. There is also a custom HTTP header, X-QT-SR: 14, that does not appear in earlier campaigns. Whether this is TeamPCP operating under deliberate plausible deniability or a second actor who obtained their tools and adapted them, nobody has confirmed yet.
What is not in dispute is how the attack works.
The malicious code was committed to the Xinference repository on April 22 at 04:08:08 UTC by a bot account called XprobeBot, which had been active as a legitimate automated contributor since October 2025. The account was almost certainly compromised, then used to push a malicious commit directly into xinference/__init__.py. That file is the first thing Python loads the moment you type import xinference, before any of your own code runs. There is no trigger, no condition, no interaction required. You install the package and import it, and the malware is already executing in the background.
The payload works in two stages. The first stage is a base64-encoded string that gets decoded and handed off to a completely separate Python process running in the background, with all output suppressed so nothing appears in your terminal. That subprocess decodes the second stage, which is the actual credential harvester. The attackers published three consecutive versions in a single day, 2.6.0, 2.6.1, and 2.6.2, with the injection point moving between them. In 2.6.0 the payload sits at bare module scope. By 2.6.2 it is hidden inside what looks like a legitimate helper function. They were watching how detection tools responded and adjusting in real time.
The harvester goes after everything it can reach. AWS credentials. Google Cloud configurations. Kubernetes tokens and service account files. Every environment variable currently loaded in memory. SSH private and public keys. API keys and secrets from .env files and config directories. Cryptocurrency wallets for Bitcoin, Ethereum, Litecoin, Dogecoin, and Monero. Database passwords for SQL, Redis, MongoDB, and LDAP. Shell history from .bash_history and .zsh_history files. SSL certificates. Slack and Discord webhook URLs. System metadata including usernames, device info, IP addresses, and network interface details. And this is the part that surprises people: the malware does not just read files sitting on disk. It makes live API calls to AWS Secrets Manager and AWS SSM Parameter Store and pulls the secrets directly out of there too. Everything gets compressed into an archive and sent via HTTP POST to the attacker-controlled domain whereisitat.lucyatemysuperbox.space.
There is no backdoor in this payload, no ransomware, no persistence mechanism, no reverse shell. It does not want your machine. It wants your secrets. In and out, silent, and by the time you notice something is off the data is already gone.
One more thing worth knowing: in earlier phases of this campaign, the attackers used something called CanisterWorm, which runs its command and control traffic through the Internet Computer Protocol, a blockchain-based network. Normal attacker infrastructure gets taken down by registrars and hosting companies. ICP canisters cannot be taken down that way. It is the first documented use of ICP as C2 in a supply chain campaign, and it means at least part of the infrastructure for this operation is designed to be permanently online.
Here is how to check if you are affected.
- → Run
pip show xinferenceand look at the version field. Safe is anything at or below 2.5.0. If you see 2.6.0, 2.6.1, or 2.6.2, act as if the environment is already compromised. - → If you ran a compromised version, rotate everything immediately. AWS access keys, GCP service account credentials, Kubernetes tokens, SSH keys, API keys for any service your machine had access to, database passwords, and any tokens stored in environment variables or .env files. Rotate all of it, not just the ones you think were at risk.
- → Downgrade to version 2.5.0 with
pip install xinference==2.5.0and clear your pip cache withpip cache purgebefore reinstalling. - → Check your cloud environments, CI/CD pipelines, and version control systems for any access that looks out of place, particularly anything from the last 24 to 48 hours.
- → Pin your package dependencies to specific versions. A
requirements.txtfile that lists an exact version number instead of a range or no version at all means you cannot accidentally install a malicious update the next time something auto-updates. - → If you are running Emerging Threats IDS rules, check that ruleset v11177 is applied. It includes rules 2068899 through 2068903 specifically covering the Xinference and TeamPCP campaign C2 domains.
- → Audit any CI/CD pipelines that were pulling Xinference automatically. If a pipeline installed a compromised version, every secret that pipeline had access to should be treated as stolen.
The broader lesson from this campaign is not about Xinference specifically. It is about the assumption baked into how developers work with package managers. When you run pip install, you are trusting that the package on PyPI is what it claims to be, published by who it claims to be, containing only what the maintainers intended. That trust is not technically guaranteed. Maintainer accounts can be compromised. Bot accounts with commit access can be hijacked. CI/CD pipelines can be used to push malicious releases that look completely legitimate to every automated check. This campaign showed that attacking the tools developers trust to keep them safe is an effective way to bypass every defense those developers have in place.
How attackers get into systems through supply chain and dependency abuse, how credential harvesting malware is structured, how post-exploitation works once stolen keys land in the wrong hands, and what defenders actually need to look for is exactly what I cover in my ethical hacking course. You will work through exploitation and post-exploitation hands-on, including credential harvesting with Mimikatz, Windows and Linux privilege escalation, LOLBins, network pivoting, and persistence techniques. The course runs from absolute zero to a level where you understand what attackers actually do once they are inside a system.
Hacking is not a hobby but a way of life.
Sources: JFrog Security Research · Endor Labs · SANS Institute
→ Stay updated!
Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.