What Happened
The Shai-Hulud infostealer worm has expanded its credential harvesting from 189 to 469 locations in developer environments, as reported by GitGuardian. This isn't a new threat actor or technique; it's an existing worm that's improved at finding static credentials that unlock your build pipelines, package repositories, and cloud infrastructure.
The worm targets credentials in CI/CD configurations, cloud provider configs, container registries, and AI tool settings. Once it finds valid credentials, it can publish malicious packages, modify existing ones, or access production environments. The attack method is simple: steal credentials, use them to compromise trusted software distribution channels, and spread to downstream victims.
Timeline
Initial variants (2023): Scanned 189 credential paths, focusing on basic Git configurations and AWS credential files.
Current variant (2024): Now checks 469 locations, adding Docker registry tokens, PyPI publishing credentials, NPM automation tokens, Kubernetes service account keys, and AI development tool credentials. This is a 148% increase in the attack surface.
The worm doesn't need to change its core infection method. It just needs to know where you store credentials, and developers keep adding new places.
Which Controls Failed
Lack of credential inventory: Organizations often don't know where credentials are stored across their infrastructure. If you can't list your credential storage locations, you can't protect them. The increase from 189 to 469 paths shows attackers have a better inventory than most security teams.
Static, long-lived credentials in CI/CD: Package publishing tokens that never expire, Docker registry credentials hard-coded in pipeline configs, and cloud provider keys stored in environment variables are all vulnerabilities. These credentials work the same whether used by your build process or by an attacker who stole them months ago.
Missing credential rotation policies: Without expiration, you can't limit the damage of a theft. An attacker who compromises a PyPI token in January can use it to publish packages in December unless you've enforced rotation.
No identity-backed authentication for non-human actors: Your build pipelines authenticate with a static token that proves possession of a secret rather than verifying the authorized system making the request.
What Standards Require
PCI DSS v4.0.1 Requirement 8.3.2 mandates that passwords meet minimum strength requirements and change at least every 90 days. While this specifically addresses user passwords, the principle applies to any authentication credential. Static package publishing tokens that never expire violate the intent of time-limited authentication.
NIST 800-53 Rev 5 Control IA-5(7) requires that systems store and transmit only cryptographically-protected passwords. More relevant here: IA-5(1)(d) specifies that authenticators have "minimum and maximum lifetime restrictions." Your PyPI tokens should expire, Docker registry credentials should rotate, and cloud provider keys should have defined lifespans.
ISO/IEC 27001:2022 Control 5.17 (Authentication Information) states that "allocation and management of authentication information shall be controlled by a management process." This means you need to know what credentials exist, where they're used, and when they expire. The Shai-Hulud expansion from 189 to 469 paths shows that most organizations haven't implemented this control for non-human identities.
SOC 2 Type II CC6.1 requires that "the entity implements logical access security measures to protect against threats from sources outside its system boundaries." Static credentials scattered across 469 locations represent a failure of logical access controls. You can't protect what you can't list, and you can't contain what doesn't expire.
Lessons and Action Items
Build a credential inventory this week. Start with package publishing credentials for NPM, PyPI, RubyGems, Maven Central, and any internal registries. Map every location where these credentials could exist: CI/CD configs, developer machines, container images, infrastructure-as-code repos. You're looking for the same 469 paths Shai-Hulud checks. GitGuardian's research gives you a head start on the list.
Implement OpenID Connect (OIDC) for package publishing. NPM, PyPI, and Docker Hub all support OIDC-based authentication from GitHub Actions. This replaces static tokens with short-lived credentials that prove your build pipeline's identity. The token expires after the job completes. If an attacker steals it, it's already useless. GitHub Actions to NPM via OIDC takes about 30 minutes to configure. Do it for your highest-risk packages first.
Set maximum lifetimes for all static credentials that remain. If you can't migrate to OIDC immediately, force rotation. Set all package publishing tokens to expire in 90 days. Set calendar reminders for 75 days. When you rotate, use a password manager or secrets vault to generate and store the new credential. Don't store it in your CI/CD config as plaintext.
Audit your CI/CD environment variables. Run env in your build jobs and review everything that looks like a credential. If it's a static token, document what it's for and when it expires. If it doesn't expire, add it to your OIDC migration list. If you don't know what it's for, that's a separate problem that needs immediate attention.
Prioritize package publishing credentials above everything else. An attacker with your NPM publishing token can distribute malware to everyone who depends on your packages. This is a supply chain attack vector with massive downstream impact. Your AWS credentials might compromise your infrastructure. Your NPM credentials compromise your customers' infrastructure.
The Shai-Hulud expansion isn't about a sophisticated new attack. It's about attackers doing basic reconnaissance better than you're doing basic defense. They know where credentials hide. You should too.



