Skip to main content
19 PyPI Packages Trojanized: The Shai-Hulud BreakdownIncident
5 min readFor DevOps Leaders

19 PyPI Packages Trojanized: The Shai-Hulud Breakdown

What Happened

Socket discovered 19 compromised packages on PyPI, downloaded hundreds of thousands of times, delivering malware designed to steal developer credentials. These packages targeted scientific and bioinformatics communities, executing malicious code automatically when Python started. The attack exfiltrated GitHub tokens, cloud credentials, and other developer secrets through an obfuscated JavaScript payload.

The attackers used a *-setup.pth file—a legitimate Python mechanism for executing code during interpreter startup—combined with a hidden _index.js file containing the credential-harvesting logic. Once installed, the malware ran silently on every Python session, collecting secrets and transmitting them to attacker-controlled infrastructure.

Timeline

Initial Compromise: Attackers published trojanized versions of legitimate scientific packages to PyPI, mimicking names commonly used in bioinformatics workflows.

Distribution Period: Packages accumulated hundreds of thousands of downloads as developers installed what appeared to be routine dependencies.

Discovery: Socket's automated monitoring detected anomalous behavior in the packages—specifically, the presence of .pth files executing obfuscated JavaScript at startup.

Response: PyPI removed the malicious packages following Socket's disclosure. Organizations using these packages faced an immediate incident response requirement: assume credential compromise, rotate all secrets, and audit access logs.

Which Controls Failed or Were Missing

Dependency Verification

The affected organizations lacked automated verification of package integrity before installation. There was no cryptographic signature validation, no hash verification against known-good versions, and no review of package contents before deployment.

Your CI/CD pipeline likely pulls dependencies with pip install -r requirements.txt and trusts whatever PyPI serves. The Shai-Hulud packages exploited this trust completely.

Runtime Monitoring

The malware executed on every Python interpreter startup. Standard endpoint detection tools missed it because the execution pattern looked identical to legitimate Python initialization. No process monitoring flagged the credential access patterns. No network monitoring caught the exfiltration because the payload communicated over standard HTTPS.

Least Privilege

Developers ran Python with access to:

  • GitHub personal access tokens stored in environment variables
  • AWS credentials in ~/.aws/credentials
  • SSH keys in ~/.ssh/
  • Browser-stored credentials
  • Local git repositories containing secrets

The malware harvested everything it could reach. There was no credential isolation, no secrets management solution, and no restriction on which processes could read sensitive files.

Supply Chain Visibility

Teams had no inventory of their transitive dependencies. Installing one scientific package pulled in dozens of sub-dependencies. When one of those sub-dependencies turned malicious, nobody noticed until Socket's external analysis.

What the Standards Require

NIST 800-53 Rev 5: SR-3 (Supply Chain Controls)

SR-3 requires organizations to establish controls throughout the system development life cycle to identify and manage supply chain risks. Specifically:

SR-3(1) calls for "employing anti-counterfeit technologies and processes" and establishing "a process for identifying and documenting counterfeit components."

For your Python environment, this means:

  • Verifying package signatures before installation
  • Maintaining hash manifests of approved dependencies
  • Scanning package contents for unexpected files (like .pth files or JavaScript in Python packages)

SR-3(2) requires "limiting the harm from potential adversaries by identifying, tracking, and controlling the distribution and installation of software and firmware components."

You need a software bill of materials (SBOM) for every application, updated automatically as dependencies change.

NIST CSF v2.0: ID.SC-4

"Suppliers and third-party partners of information systems, components, and services are routinely assessed using audits, test results, or other forms of evaluations to confirm they meet their contractual obligations."

PyPI packages are third-party components. You must assess them before use, not just install blindly. This means:

  • Automated scanning of new packages and versions
  • Review of package maintainer history
  • Comparison against known-good package hashes
  • Monitoring for behavioral anomalies post-installation

ISO 27001: A.5.19 (Information Security in Supplier Relationships)

Control A.5.19 requires "processes and procedures to manage information security risks associated with the use of supplier's products or services."

For open-source dependencies, your process must include:

  • Due diligence before adopting new packages
  • Continuous monitoring of existing dependencies
  • Incident response procedures for compromised packages
  • Contractual agreements (where applicable) or documented risk acceptance

PCI DSS: Requirement 6.3.2

"An inventory of bespoke and custom software, and third-party software components incorporated into bespoke and custom software is maintained to facilitate vulnerability and patch management."

If your applications process payment data, you must maintain an inventory of all dependencies, including Python packages. When Socket disclosed the Shai-Hulud packages, you needed that inventory to determine exposure within hours, not days.

Lessons and Action Items for Your Team

1. Lock Your Dependencies Today

Replace requests>=2.28.0 with requests==2.31.0 and pin every transitive dependency. Generate a requirements.txt with exact versions and cryptographic hashes:

pip freeze > requirements.txt
pip hash -r requirements.txt > requirements-hashed.txt

Install using pip install --require-hashes -r requirements-hashed.txt. This prevents automatic upgrades to trojanized versions.

2. Scan Every Package Before Installation

Deploy a tool like Socket, Snyk, or OWASP Dependency-Check in your CI/CD pipeline. Configure it to fail builds when packages contain:

  • Unexpected file types (.pth files, JavaScript in Python packages)
  • Obfuscated code
  • Network connections during installation
  • Access to sensitive file paths

3. Isolate Credentials from Development Environments

Stop storing secrets in environment variables and dotfiles. Implement:

  • A secrets management solution (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault)
  • Short-lived credentials that expire after hours, not months
  • Separate credentials for development, staging, and production
  • Process-level restrictions on which applications can read credential files

If the Shai-Hulud malware ran on your machine tomorrow, it should find nothing to steal.

4. Monitor Python Startup Behavior

Add monitoring for:

  • Creation or modification of .pth files in site-packages directories
  • Python processes making network connections during interpreter initialization
  • Unusual file access patterns during Python startup

Alert your security team when Python reads from .ssh/, .aws/, or .gitconfig during startup—legitimate packages have no reason to access these paths.

5. Build Your SBOM Pipeline

Implement automated SBOM generation for every application:

  • Generate SBOMs in CI/CD using tools like Syft or CycloneDX
  • Store SBOMs in a searchable database
  • When a vulnerability or compromise is disclosed, query your SBOM database to identify affected applications in minutes

When the next Shai-Hulud campaign hits, you need to answer "Are we exposed?" before your CISO asks.

6. Establish Package Approval Workflows

Create a process where new dependencies require security review before production use:

  • Automated scanning catches obvious malware
  • Manual review examines package maintainer history, GitHub activity, and community reputation
  • A waiting period (48-72 hours) for new package versions before approval
  • Exception process for urgent security patches

This won't catch every attack, but it raises the bar significantly.


The Shai-Hulud campaign succeeded because it exploited the gap between "we use open source" and "we secure open source." Your team likely has that same gap. Close it before the next campaign targets your dependencies.

Topics:Incident

You Might Also Like