Shai-hulud 2.0 Campaign Targets Cloud and Developer Ecosystems
Shai-hulud 2.0 campaign features a sophisticated variant capable of stealing credentials and secrets from major cloud platforms and developer services, while automating the backdooring of NPM packages maintained by victims. Its advanced tactics enable rapid, stealthy propagation across the software supply chain, putting countless downstream users at risk.
---
Cyber Threats
Shai-hulud 2.0 Campaign Targets Cloud and Developer Ecosystems
Shai-hulud 2.0 campaign features a sophisticated variant capable of stealing credentials and secrets from major cloud platforms and developer services, while automating the backdooring of NPM packages maintained by victims. Its advanced tactics enable rapid, stealthy propagation across the software supply chain, putting countless downstream users at risk.
By: Jeffrey Francis Bonaobra
Nov 27, 2025
Read time: ( words)
[Image: Share]
[Image: Print]
Save to Folio
---
Key takeaways:
- Shai-hulud 2.0 continues the first variant’s credential theft by stealing credentials and secrets from major cloud platforms as well as NPM tokens and GitHub authentication credentials, but introduces backdoor capabilities.
- The malware automates supply chain compromise by backdooring all NPM packages maintained by the victim, republishing them with malicious payloads that execute on installation, creating a highly wormable threat with the potential to impact thousands of downstream users.
- The malware uses stolen cloud credentials to access cloud-native secret management services, while also exhibiting destructive code that wipes user data when unsuccessful in harvesting data.
- Trend Vision One™ detects and blocks the indicators of compromise (IOCs) outlined in this blog, and provides customers with tailored threat hunting queries, threat insights, and intelligence reports.
This blog continues our investigation on the Node Package Manager (NPM) supply chain attack that took place on September 15, where attackers executed a highly targeted phishing campaign to compromise the account of an NPM package maintainer. Our previous blog detailed how the malicious code injected onto JavaScript packages diverted cryptocurrency assets by hijacking web APIs and manipulating network traffic, and how the Shai-hulud worm in the attack payload steals cloud service tokens, deploys secret-scanning tools, and spreads to additional accounts. An incident this November 24 reported hundreds of NPM repositories compromised by what appears to be a new Shai-hulud campaign with the repository description, "Sha1-Hulud: The Second Coming."
This blog discusses Trend™ Research findings on Shai-hulud 2.0 and reveals new functions that weren’t observed in its first variant.
Our analysis of Shai-hulud 2.0 reveals that it can steal credentials from AWS, GCP, and Azure cloud providers, which can contain API keys, tokens, and passwords, along with NPM tokens and GitHub authentication credentials. The malware also creates GitHub Actions workflows that allow for command-and-control (C&C). It also injects GitHub Actions workflow mechanisms that are specifically designed to steal repository secrets.
Beyond stealing static credentials, the malware uses stolen cloud credentials to access cloud-native secret management services: it can retrieve secrets from AWS using the AWS Secrets Manager API, extracts Google Cloud secrets through the GCP Secret Manager API, and collects Azure secrets via Azure Key Vault. The malware also targets credentials from Azure Pod Identity, a legacy system that remains widely used for providing Azure identities to Kubernetes pods.
To top all the capabilities, the malware also automatically backdoors every NPM package maintained by the victim, republishing them with malicious payloads that run during package installation, creating a wormable vector capable of spreading exponentially across the NPM ecosystem and potentially compromising thousands of downstream users who trust the affected packages. This entire workflow is automated and parallelized across up to 100 packages at once, maximizing propagation while keeping detection opportunities minimal.
Shai-hulud 2.0 attack chain analysis
Shai-Hulud 2.0 is delivered as an NPM package with a malicious preinstall script that executes automatically during the NPM installation process (modified package.json with "preinstall": "node setup_bun.js").
[Image: Figure 1. Shai-hulud 2.0 high-Level workflow ]
Figure 1. Shai-hulud 2.0 high-Level workflow
download
setup_bun.js (Loader)
The initial dropper setup_bun.js script serves as a loader that executes during the NPM package installation process. Its primary purpose is to ensure that a Bun JavaScript runtime is available on the victim's system and then uses it to execute the main malware payload (bun_environment.js).
[Image: Figure 2. setup_bun.js execution flow ]
Figure 2. setup_bun.js execution flow
download
Bun runtime detection and installation
The initial phase of the setup script involves detecting whether a Bun JavaScript runtime is already installed on the victim's system. This detection process uses platform-specific commands to search the system PATH for the Bun executable. On Windows systems, the script executes the where command, while Unix-like systems (Linux and macOS) use the which command.
If Bun is not detected in the system PATH, the script initiates an automatic download and installation process. This process uses official Bun installation scripts provided by bun.sh, which adds legitimacy to the installation and reduces the likelihood of detection by security software.
On Windows, the script uses PowerShell to download and execute the installation script via the Invoke-RestMethod and Invoke-Expression cmdlets. On Unix-like systems, it uses curl to download the installation script and pipes it directly to bash for execution. This approach mirrors the official Bun installation instructions, making the malware's behavior appear legitimate.
[Image: Figure 3. Checks if Bun is available on system PATH before attempting installation ]
Figure 3. Checks if Bun is available on system PATH before attempting installation
Reloading PATH environment
After installing the Bun runtime, the setup script must reload the system PATH environment variable to detect the newly installed executable. This is necessary because the Bun installation process modifies the user's PATH, but these changes are not immediately visible to the already-running Node.js process executing the setup script. The PATH reloading mechanism uses platform-specific techniques to retrieve the updated environment configuration.
On Windows systems, the script queries the Windows Registry using PowerShell to retrieve both user-level and machine-level PATH variables, then combines them into a single PATH string. This approach ensures that the Bun installation is detected regardless of whether it was installed to the user's profile directory or a system-wide location. The script uses the [Environment]::GetEnvironmentVariable() .NET method accessed through PowerShell to read these registry values.
[Image: Figure 4. Powershell execution to query updated PATH from registry ]
Figure 4. Powershell execution to query updated PATH from registry
download
On Unix-like systems, the PATH reloading process is more complex due to the variety of shell configurations that might be in use. The script attempts to source common shell profile files including .bashrc, .bash_profile, .profile, and .zshrc in the user's home directory.
[Image: Figure 5. Powershell execution to query updated PATH from registry ]
Figure 5. Powershell execution to query updated PATH from registry
Payload execution
The final phase of the setup script orchestrates the execution of the actual malware payload.
The script first checks if Bun is available in the system PATH which would be the case if Bun was previously installed or just successfully installed in an earlier step. If not found in PATH, the script searches for a locally bundled Bun executable that might have been included with the malicious package. The local Bun search examines several possible file paths within a bun-dist directory that could be bundled with the package. This includes checking for both Unix-style executable names (bun) and Windows executable names (bun.exe) in various subdirectory structures.
[Image: Figure 6. Checking if Bun is available in the system PATH ]
Figure 6. Checking if Bun is available in the system PATH
Once a Bun executable is located or installed, the script proceeds to execute the main malware payload stored in bun_environment.js. The script spawns the Bun process with the payload file as an argument.
[Image: Figure 7. Execution of the main payload bun_environment.js ]
Figure 7. Execution of the main payload bun_environment.js
bun_environment.js (main payload)
jy1() function
The jy1() function is the main entry point of the Shai-hulud 2.0 malware payload. When bun.exe bun_environment.js executes, this function is invoked at the script's top level and orchestrates the entire attack sequence.
CI/CD environment checking
The malware checks for CI/CD environment variables to determine if it's running in a continuous integration pipeline or on a developer's local machine. When detected, the malware executes immediately without background spawning to maximize credential access during build pipelines.
[Image: Figure 8. Checking of CI/CD environments ]
Figure 8. Checking of CI/CD environments
download
- BUILDKITE - Buildkite CI/CD platform
- PROJECT_ID - Google Cloud Build
- GITHUB_ACTIONS - GitHub Actions workflows
- CODEBUILD_BUILD_NUMBER - AWS CodeBuild
- CIRCLE_SHA1 - CircleCI pipelines
If none of these variables exist in process.env, it concludes the environment is a regular developer machine rather than a CI/CD pipeline, which triggers the stealth execution path with background process spawning.
Developer machine execution
The malware uses a stealth approach on developer machines by spawning a detached background process using Bun.spawn().unref() with the POSTINSTALL_BG=1 environment variable flag, which allows the parent process to exit immediately so NPM install completes in normal timing (2-3 seconds, avoiding user suspicion). This happens while the background child process, which inherited all environment variables including credentials, continues running completely hidden from the user to steal credentials.
[Image: Figure 9. Spawning of detached background process ]
Figure 9. Spawning of detached background process
aL0() function
aL0() is an asynchronous JavaScript function that targets CI/CD environments and developer workstations to steal credentials from AWS, GCP, and Azure cloud providers, along with NPM tokens and GitHub authentication credentials. When authentication fails, the malware executes destructive commands to wipe user data.The function begins by detecting if it's running in a GitHub Actions CI environment.
The malware uses 3 functions to establish persistence and disable security controls on Linux systems. The cQ0() function performs process detection by checking if the malware agent is already running on the system by searching for "/home/agent/agent" in the process list.
[Image: Figure 10. Function used to check for /home/agent/agent in the process list ]
Figure 10. Function used to check for /home/agent/agent in the process list
The pQ0() function attempts privilege escalation by first testing whether passwordless sudo access is available, and if not, it exploits Docker's privileged container access to mount the host filesystem and modify the sudoers configuration file to grant itself unrestricted root privileges.
[Image: Figure 11. Function used for privilege escalation ]
Figure 11. Function used for privilege escalation
download
Finally, the gQ0() function disables security controls by stopping and reconfiguring the systemd-resolved DNS service with a malicious configuration file, then systematically flushing iptables firewall rules in both the OUTPUT and DOCKER-USER chains to remove any network filtering that could block command-and-control (C&C) communications or data exfiltration.
[Image: Figure 12. Function used to disable security controls ]
Figure 12. Function used to disable security controls
Next, the malware instantiates modules for three major cloud providers and GitHub to prepare credential harvesting modules for AWS, Google Cloud Platform, Azure, and GitHub.
[Image: Figure 13. Instantiation of modules for credential harvesting ]
Figure 13. Instantiation of modules for credential harvesting
NPM token retrieval
The malware uses a credential theft module that targets NPM authentication tokens from developer workstations. It searches for .NPMrc configuration files in two locations: the current working directory and the user's home directory, which are standard locations where NPM stores registry authentication credentials. The function reads these files line by line, skipping comments and empty lines, then uses a regular expression pattern to extract NPM authentication tokens that match the format _authToken= or :_authToken= followed by a base64-encoded token string.
[Image: Figure 14. Function used to extract NPM authentication tokens ]
Figure 14. Function used to extract NPM authentication tokens
download
The malware then validates the token by calling NPM's /-/whoami API endpoint to verify the token works and retrieve the authenticated username. The whoami function verifies NPM authentication and retrieves the victim's NPM username, which is then used to query for packages they maintain. If validation succeeds, the username is stored and used in a later stage to enumerate all packages the victim maintains (up to 100 packages) and the validated token is then used to publish backdoored versions of those packages to the NPM registry.
[Image: Figure 15. Validates victim's NPM token and retrieves username for package enumeration ]
Figure 15. Validates victim's NPM token and retrieves username for package enumeration
Command-and-control
[Image: Figure 16. C&C Installation execution flow ]
Figure 16. C&C Installation execution flow
download
The malware creates a GitHub repository and establishes C&C infrastructure. It first verifies that GitHub authentication is available to ensure the malware has valid stolen GitHub credentials before proceeding. It then generates an 18-character random identifier that will serve as the unique repository name for this specific infected victim, ensuring each compromised machine has its own dedicated C&C repository.
- Repository creation via GitHub API
The createRepo() function uses the GitHub Octokit API to call repos.createForAuthenticatedUser() with the random name, the description "Shai-Hulud: The Second Coming.", and specific configuration settings including public visibility, discussions enabled, and all other features like issues, projects, and wiki disabled to keep the repository minimal and focused on C&C operations.
[Image: Figure 17. Creation of attacker-controlled Git repository using the GitHub Octokit API ]
Figure 17. Creation of attacker-controlled Git repository using the GitHub Octokit API
After successful creation, it extracts the repository owner's login username and the repository name from the API response, then constructs the full repository path in the format "owner/repo" and stores it in this.gitRepo for future reference.
[Image: Figure 18. Initialization and registration of the attacker-created GitHub repository ]
Figure 18. Initialization and registration of the attacker-created GitHub repository
- Workflow permission verification
It calls checkWorkflowScope() to validate whether the stolen GitHub token has the workflow OAuth scope permission, which is required to manage GitHub Actions workflows and runners. It sends a HEAD request to GitHub's API and examines the x-oauth-scopes response header, which contains a comma-separated list of token permissions. The function parses this header, checks if "workflow" is present in the list, and caches the result in this.hasWorkflow. If the workflow scope exists, the malware can proceed with deploying self-hosted runners and C&C workflows.
[Image: Figure 19. Workflow scope detection enabling deployment of malicious runners and C&C workflows ]
Figure 19. Workflow scope detection enabling deployment of malicious runners and C&C workflows
download
The malware then requests a runner registration token from GitHub's API using a POST request to /repos/{owner}/{repo}/actions/runners/registration-token, which returns a temporary token needed to register a self-hosted runner with the repository.
[Image: Figure 20. Requesting a GitHub runner registration token for self-hosted runner deployment ]
Figure 20. Requesting a GitHub runner registration token for self-hosted runner deployment
download
- Platform-specific runner deployment
Based on the detected operating system (Linux, Windows, or macOS), the malware downloads the appropriate GitHub Actions runner binary from the official GitHub releases, extracts it to a hidden directory ($HOME/.dev-env on Unix or the user's home directory on Windows), and configures it with the registration token to connect back to the newly created repository with the runner name "SHA1HULUD". The runner is launched in the background using nohup on Unix systems or a hidden PowerShell window on Windows, ensuring it continues running even after the initial malware process exits and establishing persistent access to the compromised machine.
- C&C workflow installation
The malware creates a GitHub Actions workflow file at .github/workflows/discussion.yaml in the repository that triggers whenever a discussion is created, allowing the attacker to execute arbitrary commands on the infected machine by simply posting messages in the repository's discussion section, completing the C&C infrastructure setup.
[Image: Figure 21. Creation of a malicious GitHub Actions workflow enabling command execution via discussion posts ]
Figure 21. Creation of a malicious GitHub Actions workflow enabling command execution via discussion posts
download
- Anti-analysis failsafe
If an NPM token is invalid or not found, and the malware also fails to obtain a valid GitHub token, it executes destructive commands that attempt to delete all files in the user's home directory.
[Image: Figure 22. Executed destructive commands if GitHub authentication fails and no NPM token is found ]
Figure 22. Executed destructive commands if GitHub authentication fails and no NPM token is found
download
- System information collection
The malware then collects detailed system information and GitHub credentials to build a comprehensive profile of the victim system including platform details, hostname, OS user information, and GitHub authentication status.
[Image: Figure 23. Collection of system metadata and GitHub credentials for victim profiling ]
Figure 23. Collection of system metadata and GitHub credentials for victim profiling
Malicious workflow creation for secret exfiltration
In addition to the discussion.yaml workflow shown above, the malware creates another GitHub Actions workflow specifically designed to steal all repository secrets. The workflow injection mechanism leverages GitHub's native CI/CD infrastructure to automatically exfiltrate sensitive data whenever code is pushed to compromised repositories.
[Image: Figure 24. Malicious workflow injection to retrieve GitHub secrets ]
Figure 24. Malicious workflow injection to retrieve GitHub secrets
download
Workflow injection mechanism
The malware systematically iterates through all repositories accessible to the compromised GitHub account, creating malicious workflow files in each one. To avoid immediate detection, the malware first creates a temporary branch with a timestamped name following the pattern add-linter-workflow-{timestamp}. This branch naming convention appears legitimate, mimicking common development practices where developers create feature branches for adding code quality tools like linters and formatters. By injecting the malicious workflow on a separate branch rather than directly on the main branch, the malware reduces the risk of triggering branch protection rules or immediate code review processes that might expose the malicious activity.
[Image: Figure 25. Temporary Branch Creation for Workflow Injection ]
Figure 25. Temporary Branch Creation for Workflow Injection
Malicious workflow content
The malicious workflow file is carefully crafted to appear as a legitimate code formatting tool while concealing its true purpose of credential exfiltration. The workflow is named "Code Formatter" and configured to trigger on every push event, ensuring it executes automatically whenever any developer commits code to the repository. The core of the attack leverages GitHub Actions' built-in secrets context, which provides access to all repository secrets configured in the repository settings. By using the toJSON() function, the malware serializes the entire secrets object into a JSON string, capturing every credential stored in the repository including cloud provider keys, API tokens, database passwords, and signing certificates.
[Image: Figure 26. Disguises secret exfiltration as code formatting workflow using toJSON(secrets) ]
Figure 26. Disguises secret exfiltration as code formatting workflow using toJSON(secrets)
Workflow execution monitoring
After injecting the malicious workflow file into the target repository, the malware enters a monitoring phase where it actively polls the GitHub Actions API to detect when the workflow executes and completes. The malware implements a polling loop with a 30-second timeout, repeatedly querying the repository's workflow runs every 2 seconds to identify the newly created workflow execution. This monitoring mechanism is necessary because the workflow only triggers when code is pushed to the repository, and the malware needs to wait for either an existing automated process or a developer to push code that will trigger the malicious workflow.
[Image: Figure 27. Monitors GitHub Actions API with 30-second timeout to detect workflow execution and completion ]
Figure 27. Monitors GitHub Actions API with 30-second timeout to detect workflow execution and completion
Artifact download and secret extraction
Once the workflow begins execution, the malware tracks its progress by checking the workflow status, waiting for it to transition to "completed" before attempting to download the exfiltrated secrets from the workflow artifacts.
[Image: Figure 28. Polls workflow run status to ensure execution completed before artifact download ]
Figure 28. Polls workflow run status to ensure execution completed before artifact download
Once the workflow is completed, the malware queries the GitHub Actions Artifacts API to list all artifacts produced by the workflow run. GitHub Actions allows workflows to upload multiple artifacts, so the malware must enumerate the artifact list to identify the specific artifact containing the stolen secrets. The API returns an array of artifact objects, each containing metadata such as the artifact ID, name, size, and download URL. The malware iterates through this array looking for the artifact named "formatting" that was uploaded by the malicious workflow's actions/upload-artifact@v5 step.
[Image: Figure 29. Polls workflow run status to ensure execution completed before artifact download ]
Figure 29. Polls workflow run status to ensure execution completed before artifact download
GitHub implements a two-step download process for artifacts to provide secure, time-limited access to artifact storage. The malware first requests the artifact download endpoint with the artifact ID, setting the redirect parameter to "manual" to prevent automatic redirect following. This initial request returns a Location header containing a temporary, pre-signed URL that points to the actual artifact storage location. The malware extracts this redirect URL from the response headers, then makes a second HTTP request to this temporary URL to download the actual ZIP file containing the stolen secrets.
[Image: Figure 30. Retrieves redirect URL then downloads ZIP file from temporary storage location ]
Figure 30. Retrieves redirect URL then downloads ZIP file from temporary storage location
After successfully downloading the artifact ZIP file, the malware must extract the format.json file containing the stolen repository secrets. The download response is received as an ArrayBuffer, which the malware converts to a Node.js Buffer object for processing. Using a ZIP archive library (referenced as tG0["default"]), the malware opens the ZIP archive and calls getEntry("format.json") to locate the specific file containing the exfiltrated secrets. Once the entry is found, the malware extracts the file data and converts it from binary to a UTF-8 string, revealing the JSON-formatted repository secrets that were captured by the malicious workflow.
Multi-cloud credential harvesting
The malware simultaneously harvests credentials from all three major cloud providers by first capturing the complete set of environment variables, which may contain API keys, tokens, and passwords. It then retrieves secrets from AWS using the AWS Secrets Manager API, extracts Google Cloud secrets through the GCP Secret Manager API, and collects Azure secrets via Azure Key Vault.
[Image: Figure 31. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager APIs ]
Figure 31. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager APIs
AWS credential collection
The malware implements AWS credential harvesting by leveraging the AWS SDK's built-in credential provider chain. This mechanism automatically searches for credentials in environment variables following the standard AWS credential lookup order. The implementation uses a getter method that checks for the presence of required AWS environment variables (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) and extracts them along with optional session tokens.
[Image: Figure 32. Harvesting AWS credentials via the SDK’s environment variable lookup chain ]
Figure 32. Harvesting AWS credentials via the SDK’s environment variable lookup chain
The credential extraction function operates asynchronously and includes detailed logging capabilities that can be enabled through the malware's configuration. When credentials are found, the function constructs a credential object containing the access key ID, secret access key, and session token (if present). Additionally, the malware captures extended credential metadata including expiration timestamps, credential scope information, and AWS account IDs.
[Image: Figure 33. Asynchronous AWS credential extraction with optional logging, returning keys, tokens, and metadata ]
Figure 33. Asynchronous AWS credential extraction with optional logging, returning keys, tokens, and metadata
The malware also targets AWS configuration files stored in the user's home directory by implementing file path resolution functions that follow AWS CLI conventions. The implementation first checks for custom file paths specified through environment variables (AWS_CONFIG_FILE and AWS_SHARED_CREDENTIALS_FILE), then falls back to default locations in the ~/.aws/ directory.
[Image: Figure 34. AWS config file harvesting via environment‑aware path resolution following standard AWS CLI conventions ]
Figure 34. AWS config file harvesting via environment‑aware path resolution following standard AWS CLI conventions
The malware includes specialized functionality for stealing credentials from containerized environments, specifically targeting AWS ECS (Elastic Container Service) and EKS (Elastic Kubernetes Service) task credential mechanisms. The implementation checks for container-specific environment variables that point to credential endpoints exposed by the container runtime. These endpoints provide temporary credentials with IAM role permissions assigned to the container task, making them highly valuable for attackers seeking to move laterally within cloud infrastructure.
[Image: Figure 35. Container‑specific credential harvesting from ECS/EKS task ]
Figure 35. Container‑specific credential harvesting from ECS/EKS task
Google Cloud Platform (GCP) credential collection
The malware also targets Google Cloud Platform's Application Default Credentials (ADC) system, which is the recommended authentication method for GCP applications. The implementation begins by checking the GOOGLE_APPLICATION_CREDENTIALS environment variable (case-insensitive to catch variations), which typically points to a service account JSON key file. When the environment variable is found, the malware attempts to read the referenced credential file and extract the service account private key, client email, and project information.
[Image: Figure 36. GCP ADC credential theft via GOOGLE_APPLICATION_CREDENTIALS and service account key extraction ]
Figure 36. GCP ADC credential theft via GOOGLE_APPLICATION_CREDENTIALS and service account key extraction
download
The malware implements platform-aware logic to locate the Google Cloud SDK configuration directory, which stores authenticated user credentials and project settings. The function first checks for the CLOUDSDK_CONFIG environment variable that allows users to customize the configuration directory location. If this variable is not set, the malware determines the appropriate default path based on the operating system, using %APPDATA%\gcloud on Windows systems and ~/.config/gcloud on Unix-like systems.
Once the configuration directory is located, the malware accesses multiple credential stores within this directory and various authentication tokens cached by the gcloud CLI.
[Image: Figure 37. Platform‑aware discovery of the GCP SDK config directory and extraction of cached gcloud authentication tokens ]
Figure 37. Platform‑aware discovery of the GCP SDK config directory and extraction of cached gcloud authentication tokens
The malware implements a targeted file existence check and extraction routine specifically for the Application Default Credentials JSON file. The implementation includes defensive programming with an existence check (existsSync) to avoid errors when the file is not present, silently failing and setting the path to null rather than throwing exceptions that might alert monitoring systems.
[Image: Figure 38. Targeted check for the ADC JSON file ]
Figure 38. Targeted check for the ADC JSON file
Azure credential collection
The malware also implements a comprehensive Azure credential harvesting system that targets the entire spectrum of Azure authentication methods. The implementation begins by defining an array of critical Azure environment variables and systematically checking for their presence in the process environment. The credential provider includes intelligent parsing logic that handles complex configurations such as semicolon-delimited tenant lists and boolean flag conversions.
The malware logs discovered environment variables using a verbose logging system, providing attackers with visibility into which authentication methods are available on the compromised system.
[Image: Figure 39. Scanning of Azure-related environment variables ]
Figure 39. Scanning of Azure-related environment variables
The malware implements a cascading credential extraction system that attempts multiple Azure authentication methods. The implementation first attempts to extract Service Principal credentials with client secrets (the most common authentication method for automated systems), which requires a tenant ID, client ID, and client secret. If these three values are present, the malware instantiates a ClientSecrietCredential object that can be used to authenticate to Azure Resource Manager and access Azure resources with the permissions granted to the service principal. Each successful credential extraction is logged with tenant and client IDs.
[Image: Figure 40. Azure credential harvesting to extract Service Principal credentials from available environment variables ]
Figure 40. Azure credential harvesting to extract Service Principal credentials from available environment variables
download
If client secret authentication fails, the malware falls back to certificate-based authentication by checking for AZURE_CLIENT_CERTIFICATE_PATH, and optionally, AZURE_CLIENT_CERTIFICATE_PASSWORD.
[Image: Figure 41. Azure fallback to certificate‑based authentication by checking for client certificate paths and optional passwords. ]
Figure 41. Azure fallback to certificate‑based authentication by checking for client certificate paths and optional passwords.
download
The malware then attempts username and/or password authentication though it includes warning logic suggesting this method is deprecated.
[Image: Figure 42. Azure username/password authentication attempt ]
Figure 42. Azure username/password authentication attempt
download
The malware also includes support for extracting Workload Identity credentials, a modern Azure authentication mechanism used primarily in Kubernetes environments. The implementation targets three critical environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_FEDERATED_TOKEN_FILE.
[Image: Figure 43. Azure Workload Identity credential harvesting via key environment‑variable checks ]
Figure 43. Azure Workload Identity credential harvesting via key environment‑variable checks
The malware also targets credentials from Azure Pod Identity, a legacy system that remains widely used for providing Azure identities to Kubernetes pods.
The implementation checks for the AZURE_POD_IDENTITY_AUTHORITY_HOST environment variable, which points to the Instance Metadata Service (IMDS) endpoint that provides managed identity tokens. When this variable is present, the malware constructs the full OAuth token endpoint URL by appending the standard token path to the authority host.
If the environment variable is not set, the malware falls back to the standard Azure IMDS endpoint.
[Image: Figure 44. Azure Pod Identity credential harvesting via IMDS endpoint discovery and token retrieval ]
Figure 44. Azure Pod Identity credential harvesting via IMDS endpoint discovery and token retrieval
Active Cloud secret manager exploitation
Beyond stealing static credentials, the malware uses stolen cloud credentials to access cloud-native secret management services.
The implementation includes dedicated processes for AWS Secrets Manager, GCP Secret Manager, and Azure Key Vault, each implementing the listAndRetrieveAllSecrets() method to enumerate and extract secrets stored in these services.
AWS secrets manager
The AWS secret harvesting module implements a multi-region secret harvesting strategy that maximizes the number of secrets retrieved. The WX class first validates stolen AWS credentials using the STS GetCallerIdentity API call to ensure they are active and to extract identity information (User ID, Account ID, ARN).
Once credentials are validated, the malware iterates through 17 major AWS regions to discover secrets stored in regional Secrets Manager services. For each region, the malware calls the ListSecrets API to enumerate available secrets, then retrieves each secret's value using GetSecretValue.
GCP secrets manager
The GCP secret-harvesting module uses advanced authentication handling, first validating access by calling getAccessToken() from the Google Auth library. Once credentials are confirmed, the malware queries the Resource Manager API to list all accessible GCP projects, then creates a Secret Manager client for each project. It enumerates secrets with listSecrets() and retrieves their latest versions using the {secretName}/versions/latest path, extracting both the secret payload and associated metadata.
[Image: Figure 45. GCP secret harvesting via access‑token validation, project enumeration, and per‑project Secret Manager extraction ]
Figure 45. GCP secret harvesting via access‑token validation, project enumeration, and per‑project Secret Manager extraction
Azure Key Vault
The Azure secret harvesting implementation targets Microsoft's Key Vault service by first authenticating using the DefaultAzureCredential provider, which attempts multiple authentication methods in sequence (environment variables, managed identity, Azure CLI credentials). Once authenticated, the malware uses the Resource Manager API to list all Key Vault resources in the subscription by filtering for resources with type Microsoft.KeyVault/vaults. For each discovered vault, the malware instantiates a SecretClient and iterates through all secrets, retrieving their values.
[Image: Figure 46. Azure Key Vault harvesting via DefaultAzureCredential authentication, vault enumeration, and bulk secret extraction ]
Figure 46. Azure Key Vault harvesting via DefaultAzureCredential authentication, vault enumeration, and bulk secret extraction
TruffleHog secret scanning
The malware uses an automated TruffleHog deployment system that downloads, extracts, and caches the latest version of the tool directly from GitHub. It Before downloading anything, it checks its cache for an existing TruffleHog binary; if none is found, it fetches the latest release info from GitHub, downloads the correct binary for the system, extracts it, and makes it executable.
[Image: Figure 47. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager API ]
Figure 47. Simultaneous harvesting of cloud environment secrets using environment variable dumps and cloud secret‑manager API
The malware uses the GitHub API to automatically retrieve the latest TruffleHog release information without needing authentication, sending a GET request to /repos/trufflesecurity/trufflehog/releases/latest to obtain metadata and platform-specific download links. This removes the need to hard-code version numbers and ensures it remains compatible with future updates.
[Image: Figure 48. Automated TruffleHog setup with release lookup ]
Figure 48. Automated TruffleHog setup with release lookup
The malware uses TruffleHog’s filesystem scanner to sweep the victim’s home directory for hardcoded secrets, invoking the tool with JSON output for easy parsing and optional arguments to narrow or customize the scan. It runs the scan with a 10-minute timeout to avoid long, suspicious execution times, capturing both output and errors. The results are packaged into a structured report containing discovered secrets, errors, scan duration, and the tool’s exit code, allowing operators to quickly assess whether the scan succeeded, partially completed, or failed.
[Image: Figure 49. TruffleHog filesystem scan over the home directory, collecting JSON‑formatted findings, errors, duration, and exit codes ]
Figure 49. TruffleHog filesystem scan over the home directory, collecting JSON‑formatted findings, errors, duration, and exit codes
Data exfiltration
The malware implements a multi-stage exfiltration system that aggregates and uploads four distinct categories of stolen data to an attacker-controlled Git repository that the malware automatically creates under the victim's own GitHub account. The exfiltration process creates four separate JSON files, each containing specific categories of stolen data.
| File Name
| Description
| contents.json
| Aggregates system metadata such as platform details, hostname, OS user information, and GitHub authentication status.
| environment.json
| Dumps all environment variables, capturing cloud credentials, API keys, database strings, and other sensitive config data.
| cloud.json
| Contains secrets retrieved from AWS Secrets Manager, GCP Secret Manager, and Azure Key Vault, showing that the malware uses stolen credentials to access cloud-native secret stores.
| truffleSecrets.json
| Stores TruffleHog scan results, including hardcoded secrets found in source code, config files, and other documents within the user's home directory.
Supply chain propagation
[Image: Figure 50. Supply Chain Compromise of NPM packages ]
Figure 50. Supply Chain Compromise of NPM packages
download
The malware automatically backdoors every NPM package maintained by the victim, republishing them with malicious payloads that run during package installation. This creates a wormable vector capable of spreading exponentially across the NPM ecosystem and potentially compromising thousands of downstream users who trust the affected packages.
The attack unfolds in five phases: discovering all packages owned by the victim, downloading their original tarballs, injecting malicious preinstall hooks, bundling the malware installer, and republishing the modified packages as legitimate updates.
This entire workflow is automated and parallelized across up to 100 packages at once, maximizing propagation while keeping detection opportunities minimal.
Maintainer package retrieval
The malware queries the NPM registry to discover all packages maintained by the authenticated victim. The implementation uses NPM's search API with the maintainer: filter to enumerate packages owned by the compromised account. The package enumeration includes detailed metadata collection for each discovered package, including download statistics that allow the malware to prioritize high-impact targets. By sorting packages by monthly downloads in descending order, the attack maximizes potential victim count by compromising the most popular packages first.
[Image: Figure 51. NPM Package Enumeration ]
Figure 51. NPM Package Enumeration
NPM registry search
The search implementation uses NPM's official search API endpoint (/-/v1/search) with proper URL encoding and authentication headers. The default search limit of 20 packages can be overridden, and in the actual attack execution, the malware requests up to 100 packages.
[Image: Figure 52. NPM Registry Search API Implementation ]
Figure 52. NPM Registry Search API Implementation
Package download and extraction
The malware downloads the original package tarball from the NPM registry, preparing it for modification. This ensures the backdoored version maintains all original functionality while adding malicious capabilities. The download process uses standard HTTP fetching with compression support (gzip, deflate, brotli) to efficiently retrieve packages. The temporary directory naming (NPM-update-) mimics legitimate NPM tooling to avoid detection by process monitoring systems.
[Image: Figure 53. Package Tarball Download and Extraction ]
Figure 53. Package Tarball Download and Extraction
Preinstall hook injection
The core of the supply chain attack involves modifying the package's package.json file to inject a malicious preinstall script. This script executes automatically when anyone runs NPM install on the package, even before the package's actual dependencies are installed.
[Image: Figure 54. Malicious package.json Modification ]
Figure 54. Malicious package.json Modification
The preinstall script executes during the NPM installation lifecycle at the earliest possible stage, specifically:
- Before dependency resolution
- Before package installation
- Before any legitimate package code runs
- With the user's full permissions
Malicious payload bundling
The malware bundles a sophisticated multi-stage installer script (setup_bun.js) that handles Bun runtime installation and malware execution as mentioned at the beginning of this analysis. This script is designed to work across Windows, Linux, and macOS platforms.
[Image: Figure 55. Injects setup_bun.js installer and bun_environment.js malware payload into package ]
Figure 55. Injects setup_bun.js installer and bun_environment.js malware payload into package
download
Package republishing
After injecting malicious code and bundling payloads, the malware repackages the modified package and publishes it to NPM using the victim's credentials. The publishing process uses the victim's NPM authentication token obtained earlier in the attack, making the malicious update appear as a legitimate release from the trusted package maintainer. The version bump (patch increment) follows semantic versioning conventions, making it appear as a bug fix or minor update that users would typically install without scrutiny.
[Image: Figure 56. Repackages modified package and publishes to NPM using victim's credentials ]
Figure 56. Repackages modified package and publishes to NPM using victim's credentials
Proactive security with Trend Vision One™
Trend Vision One™ is the only AI-powered enterprise cybersecurity platform that centralizes cyber risk exposure management and security operations, delivering robust layered protection across on-premises, hybrid, and multi-cloud environments.
The following sections contain Trend Vision One insights, reports, and queries mentioned in the previous blog with additional information from this report.
Trend Vision One™ Threat Intelligence
To stay ahead of evolving threats, Trend customers can access Trend Vision One™ Threat Insights which provides the latest insights from Trend™ Research on emerging threats and threat actors.
Trend Vision One Threat Insights
- Shai-Hulud 2.0 npm Supply Chain Attack
Trend Vision One Intelligence Reports (IOC Sweeping)
- Shai-Hulud 2.0 npm Supply Chain Attack
Hunting Queries
Trend Vision One Search App
Trend Vision One customers can use the Search App to match or hunt the malicious indicators mentioned in this blog post with data in their environment.
malName:SHULUD AND eventName: MALWARE_DETECTION AND LogType: detection
Indicators of Compromise (IoCs)
Indicators of Compromise can be found here.
Tags
Latest News
Malware
Research
Articles, News, Reports
Cyber Threats
####
Authors
Jeffrey Francis Bonaobra
Senior Threat Response Engineer
Contact Us
Malicious OpenClaw Skills Used to Distribute Atomic macOS Stealer
LockBit Attempts to Stay Afloat With a New Version
BIOPASS RAT: New Malware Sniffs Victims via Live Streaming
See all articles
---
[Original source](https://www.trendmicro.com/en_us/research/25/k/shai-hulud-2-0-targets-cloud-and-developer-systems.html)