gitlab

Shai-Hulud copycat campaign targets Python developers through PyPI typosquatting (opens in new tab)

GitLab researchers uncovered a coordinated PyPI supply-chain campaign distributing a copy of the Shai-Hulud worm. Five packages—four typosquats and one compromised legitimate project—execute malware during Python startup, steal credentials from CI/CD and cloud environments, and propagate through developers’ repositories and package registries. The campaign demonstrates that Python packages can be weaponized without imports or explicit function calls.

Malicious PyPI Packages

  • All packages were published by the elitexp account:
    • rlask and tlask, typosquats of Flask
    • rsquests, a typosquat of Requests
    • nhmpy, a typosquat of NumPy
    • mflux-streamlit, a legitimate project later weaponized in versions 0.0.3 and 0.0.4
  • The attacker first uploaded clean probe versions matching current upstream version numbers, then replaced them with payload-bearing releases.
  • The activity followed the public release of Shai-Hulud’s source code, suggesting an independent copycat operation targeting Python users.

Python Startup-Based Infection

  • The malware uses Python .pth files, which Python processes automatically at startup.
  • The dropper:
    • Checks for a .bun_ran marker in the temporary directory.
    • Downloads the Bun JavaScript runtime from GitHub.
    • Executes a roughly 5 MB obfuscated JavaScript payload.
  • Early rlask versions also included sitecustomize.py, which searched sys.path for and executed a hidden _index.js file.
  • This approach requires no explicit package import or function invocation.

Payload Obfuscation

  • The JavaScript is protected by multiple layers:
    • Package-specific ROT-N encoding
    • AES-128-GCM encryption
    • Variable-name mangling using _0x identifiers
  • Researchers identified:
    • A small encrypted Bun downloader
    • A 772 KB Shai-Hulud credential stealer
    • Approximately 2,538 hardcoded strings

Credential Theft

The worm targets credentials and secrets from:

  • GitHub Actions tokens, repository secrets, OIDC tokens, artifacts, and runner memory
  • AWS IAM credentials, instance metadata, Secrets Manager, SSM, and STS tokens
  • Azure managed identities, Key Vault, and Microsoft Graph tokens
  • GCP service-account keys and application credentials
  • HashiCorp Vault tokens and Kubernetes authentication
  • npm, JFrog, PyPI, and RubyGems publishing credentials
  • SSH private keys and Kubernetes service-account tokens
  • Sigstore credentials and Fulcio signing certificates
  • MongoDB, MySQL, PostgreSQL, and Redis connection strings

Self-Propagation

Using stolen credentials, the worm can:

  • Add .github/setup.js and workflow files to repositories so it runs in other CI pipelines.
  • Insert .github/copilot-instructions.md to influence AI coding assistants.
  • Publish poisoned packages to PyPI, npm, and RubyGems.
  • Attempt privilege escalation on self-hosted runners through sudoers modifications.
  • Detect StepSecurity’s harden-runner and alter its behavior.

Attacker Infrastructure and Weaponized Project

  • The PyPI account was created in 2024 and was associated with the legitimate mflux-streamlit project.
  • Package uploads used Bun/1.3.14, matching the runtime downloaded by the malware.
  • Unlike a pure typosquatting campaign, the compromise of a real project could affect existing users through normal dependency updates.

Developers should audit environments for the affected packages, review CI/CD and cloud credentials, rotate exposed secrets, and enforce dependency pinning and package provenance checks. CI runners and publishing tokens should be treated as potentially compromised if any affected version was installed.