PyPI maintainer takeover: a backdoored release reaches every install
A taken-over maintainer account publishes a backdoored release that unpinned installs pull. Revoke the publish tokens, not just the password, then yank the release and advise pinning.
A package maintainer account and its publish token are non-human principals that can ship code to everyone who installs the package. When an attacker takes one over and ships a backdoor, the blast radius reaches every downstream consumer.
How the attack works
The maintainer account signs in from an IP and ASN never used before, shortly after a credential-stuffing burst. The session mints a fresh API and publish token and touches the 2FA recovery settings, establishing a durable publish path. It then uploads a backdoored patch version of a popular package whose install hook reaches out to an external host for a second-stage payload. CI systems and developers running unpinned installs pull the bad version and execute the hook. In ATT&CK terms this is T1195, Supply Chain Compromise, with T1098, Account Manipulation, T1078, Valid Accounts, and T1556 for the 2FA tampering.
Why it works
A new version, a patch bump, and a popular package are all normal. The tell is the chain: an unfamiliar-IP login after stuffing, a freshly minted publish token plus a 2FA change, and a release whose install hook calls an external host. Weak maintainer auth with long-lived tokens, plus unpinned and unverified downstream installs, is the root cause.
How to fix it
Lock the maintainer account and revoke all its API and publish tokens, because tokens, not just the password, grant upload power, then yank the malicious release and issue a consumer advisory to pin away from the bad version. Publishing a clean patch over it leaves the tokens live and the bad version installable. Scope the spread from the registry account and release-history logs cross-referenced with your internal proxy and build logs. For the class, require phishing-resistant 2FA and trusted-publishing OIDC upstream, and pin, verify signatures, and proxy downstream.
Practice it
We built this as a GraphLattice Range scenario so developers can rehearse revoking the publish tokens, yanking the release, and meeting both publisher and consumer duties.