Skip to main content
Credential Stealer Hidden in PyTorch LightningIncident
4 min readFor Security Engineers

Credential Stealer Hidden in PyTorch Lightning

On April 30, a malicious version of PyTorch Lightning appeared on the Python Package Index (PyPI). This compromised package included a credential-stealing payload targeting .env files, API keys, secrets, GitHub tokens, and browser-stored credentials. PyTorch Lightning maintainers quickly reverted the package to version 2.6.1, the last known-safe release. The exact method of compromise is still under investigation.

This incident wasn't due to a vulnerability in PyTorch Lightning's code. Instead, someone gained publish access to the legitimate package namespace and pushed a backdoored version to PyPI. Your dependency management tools may have pulled it automatically.

Timeline

April 30: Malicious version published to PyPI
April 30 (hours later): Microsoft Defender flagged suspicious behavior in environments running the compromised package
April 30 (same day): PyTorch Lightning maintainers confirmed the breach and reverted to version 2.6.1
May 1+: Organizations began rotating credentials and auditing access logs

The window between publication and detection was just hours, but that was enough time for automated CI/CD pipelines to pull, install, and run the package across multiple environments.

Which Controls Failed

Package integrity verification: The compromised package passed through PyPI's upload process without triggering integrity checks. Organizations installing it had no automated verification that the package contents matched the expected source.

Privileged access to publishing: The attacker obtained credentials or tokens with permission to publish to the PyTorch Lightning namespace. Whether through credential theft, a compromised maintainer account, or a leaked API token, the access control boundary failed.

Runtime monitoring: In environments without Microsoft Defender or similar tools, the payload executed silently. No alerts fired when a Python package started reading .env files and browser credential stores.

Dependency pinning: Teams using pip install pytorch-lightning without version pins pulled the malicious release immediately. No human review occurred between "new version available" and "running in production."

What Standards Require

NIST 800-53 Rev 5 addresses software supply chain risk in SR-3 (Supply Chain Controls and Processes). Control SR-3(1) specifically requires establishing a process to ensure the integrity of software throughout the development and deployment lifecycle. For dependencies, this means verifying package signatures and maintaining an approved software list.

PCI DSS v4.0.1 Requirement 6.3.2 mandates that software engineering techniques protect applications from attacks during development. This includes securing the development environment itself—the CI/CD pipelines and build systems that automatically pull dependencies.

ISO 27001 Control 5.19 (Information Security in Supplier Relationships) requires organizations to define and agree upon security requirements with suppliers. Open-source maintainers are suppliers in your software supply chain, even if no contract exists.

SOC 2 Type II CC7.2 (System Monitoring) requires detecting anomalies that could result from security incidents. A Python package reading credential files and making outbound connections to exfiltrate data qualifies as an anomaly your monitoring should catch.

None of these standards say "hope PyPI catches malicious packages before you install them." They all place the verification burden on you.

Lessons and Action Items

Pin Your Dependencies

Stop using pytorch-lightning>=2.0.0 in requirements.txt. Use pytorch-lightning==2.6.1. This means manual updates, which allow a human to review changelogs before your CI/CD pipeline installs a new version of a package that runs in production.

Generate a lock file with pip freeze > requirements.lock after you verify a working environment. Install from the lock file in production. Tools like Poetry and Pipenv do this automatically.

Verify Package Hashes

PyPI publishes SHA256 hashes for every package. Your installation process should verify them:

pytorch-lightning==2.6.1 \
    --hash=sha256:abc123...

Use pip-tools to generate requirements with hashes included. If the hash doesn't match, installation fails. The malicious version had a different hash than the legitimate 2.6.1 release.

Monitor File Access in CI/CD

Your build containers shouldn't touch .env files, browser credential stores, or SSH keys. Set up file integrity monitoring or runtime security tools that alert when a process accesses sensitive paths.

Microsoft Defender caught this attack by detecting unusual file access patterns. If you're running builds in Kubernetes, Falco can do the same thing. Configure rules for any Python process reading from ~/.aws/credentials, ~/.ssh/, or .env files during package installation.

Rotate Everything After a Breach

The payload targeted .env files, API keys, GitHub tokens, and browser credentials. If you ran the malicious version, assume all credentials in that environment are compromised:

  1. Rotate cloud provider credentials (AWS, GCP, Azure)
  2. Regenerate GitHub personal access tokens and deploy keys
  3. Cycle database passwords and API keys
  4. Review CloudTrail, Stackdriver, or Azure Activity logs for unauthorized access
  5. Check for new IAM users, SSH keys, or service accounts created during the exposure window

Audit logs tell you what the attacker did with stolen credentials. Check within 24 hours of discovery, not after you finish rotating everything.

Implement a Private Package Mirror

Run your own PyPI mirror with approval gates. Packages don't reach your developers until someone on the security team verifies the release. Tools like Artifactory, Nexus, or even a simple devpi server give you this control.

Yes, this adds friction. That's a feature, not a bug. The friction is a human verification step between "published to PyPI" and "running in your environment."

Use Software Bill of Materials (SBOM)

Generate an SBOM for every build. Tools like syft or cyclonedx-cli create machine-readable inventories of your dependencies. When a breach like this happens, you can query your SBOM database: "Which services are running pytorch-lightning? Which versions?"

Without an SBOM, you're grepping through git repositories and asking teams "Are you using this package?" The answer takes days instead of minutes.


The PyTorch Lightning incident proves that "trusted package from a major project" doesn't mean "safe to install without verification." Treat every dependency as untrusted until you verify it. Pin versions, check hashes, monitor runtime behavior, and maintain an inventory of what's running where.

The controls exist. Implement them before the next supply chain attack, not after.

Topics:Incident

You Might Also Like