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.