For the complete documentation index, see llms.txt. This page is also available as Markdown.
Feeding the worm a soft cloudy bun: The second coming of Shai-Hulud
Article originally co-authored with Taylor Smith on Exaforce's blog: https://www.exaforce.com/blogs/feeding-the-worm-a-soft-cloudy-bun-the-second-coming-of-shai-hulud
On November 24, 2025, Aikido Security released an article regarding a new batch of malicious packages from the threat actor now dubbed as Shai-Hulud. The malicious code tries to gather credentials from the local host it resides. Then, using those credentials, the malware tries to gather other credentials from the user’s credential manager. All the output is sent to public GitHub repositories, to which they have conveniently given descriptions of “Sha1-Hulud: TheSecondComing.”
The first known target was Zapier, with ENS, AsyncAPI, PostHog, Postman, and other vendors later confirmed. Overall, there are 492 packages compromised with a total of 132 million monthly downloads, as cited by Aikido.
Malware init
The malware is ingested in two parts in the library’s repository. The first part was the loader of the malware, stored inside setup_bun.js. This malicious code is loaded when the library is loaded and executed bun_environment.js, a heavily obfuscated script which contains the actual malicious code.
For the malicious script to be ingested by the library, it was included in the package.json file as a pre-install script. That way, before anything happened with the library, the malicious code would run.
The malware appears to be using bun to run the malicious code, hence the _bun postfix on the JS scripts. setup_bun.js will check if bun is installed and, if not, will install the tool, using the install script provided on bun.sh.
letbunExecutable;if (isBunOnPath()) { // Use bun from PATHbunExecutable='bun';}else{ // Check if we have a locally downloaded bunconstlocalBunDir=path.join(__dirname,'bun-dist');constpossiblePaths= [path.join(localBunDir,'bun','bun'),path.join(localBunDir,'bun','bun.exe'),path.join(localBunDir,'bun.exe'),path.join(localBunDir,'bun') ];constexistingBun=possiblePaths.find(p=>fs.existsSync(p));if (existingBun) {bunExecutable=existingBun;}else{ // Download and setup bunbunExecutable=awaitdownloadAndSetupBun();}}
Then, bun will run bun_environment.js, which will execute all the malicious tasks.
The bad bun
bun_environment.js is a very large file of almost 10MB of JS code. The code appears to be heavily obfuscated, with code elements like variables and function names being renamed to HEX values. The code even has a function that resolves the names of the objects from HEX to human readable (function a0_0x4cc3).
The function goes through an array of strings _0x259634 inside the function a0_0x1bc8, which contains the value in strings for the HEX value provided. In that value, we also have the exfiltration repository description 'Sha1-Hulud: The Second Coming.'
Using this function, we are able to resolve strings that are obfuscated in the script.
The last part of the code will work as the code’s main function. It will start by detecting if the code is being executed in a CI/CD environment and execute tasks accordingly.
Steal SecretsManager credentials as defined on JSON _0x4b3fc6
Steal credentials from the environment variable
Download TruffleHog and run it on the compromised machine to find stored credentials
Search for GitHub Actions Artifacts and dump them into the file results.json
Download AWS, Azure, and GCP credentials:
Retrieve system information that is later stored in systemInfo.json
Every data requested will be stored in the following files:
environment.json Environment variables and system info
truffleSecrets.json Secrets detected by TruffleHog scanner
actionsSecrets.json GitHub Actions secrets
results.json GitHub Artifacts
systemInfo.json Information about the current system
We’re having a discussion about this (GitHub) Action
The last piece of malware the threat actors are adding is the malicious GitHub Action, which executes a command on the target’s machine as soon as a discussion is created. That piece of malware is stored in the file discussion.yaml. Due to how GitHub Actions manages direct variable interpolation, if the data is provided inside the run field, it will be considered a command, as explained in this article.
What the attacker needs to do is create a GitHub discussion on the repository, with the malicious command they want to execute, and it will be executed on the compromised machines. Since the Action contains the field runs-on: self-hosted, the command will be executed on the machines where the library is installed.
To be able to manage the compromised machines through the malicious GitHub Action, the script will also download and configure GitHub Actions runner on the victim's machine, under the name SHA1HULUD on the paths defined based on the OS of the machine. This Action runner will execute the Discussion GitHub action and manage the compromised target.
Linux:$HOME/.dev-env/
Windows:%USERPROFILE%\\.dev-env\\
macOS:$HOME/.dev-env/
Data exfiltration
When all information is retrieved, the malware will create a repository
There are currently 23,000+ repositories created by this script with the compromised credentials, and there are always new ones showing up.
Next steps to mitigate the attack
The compromised library owners have removed the malicious versions and releases and are recommending that the malicious libraries be removed. It is also recommended to search your machines that may have been exposed for any traces of the malware:
setup_bun.js
bun_environment.js
cloud.json
contents.json
environment.json
truffleSecrets.json
discussion.yaml
formatter_*.yml
Search for potentially malicious libraries installed and package.json files containing them, and then remove these versions:
Search everywhere on the system for everything that contains any SHA1HULUD value or the .dev-env, which is the GitHub Action directory:
Any process that seems to be connected to the malware should be stopped and investigated:
Reset all credentials on AWS, Azure, and GCP, as well as GitHub PATs, and monitor for any action being made from any credentials you have so far. It is also recommended to search online for any repository containing the description “Sha1-Hulud: The Second Coming.”, which might contain your credentials.
Conclusions
Malicious packages seem to be the preferred technique for threat actors lately, mainly due to their worm-like behavior, large number of downloads, and the trust developers have in those libraries. As such, libraries need to be monitored, and each one of them needs to be considered as a threat, as they can be a dangerous method of compromise.
Hoping this attack did not target you, or you have it now under control, we suggest monitoring your environments and endpoints for any potential compromise.
var stringLookup = decodeString;
function decodeString(index) { // function a0_0x4cc3
var arr = stringArrayGenerator();
decodeString = function(idx) {
idx = idx - 0x0;
var value = arr[idx];
return value;
};
return decodeString(index);
}
function a0_0x1bc8() {
var _0x259634 = ['res', 'wXWSe', 'mVuzm', 'sendRequest', 'decorate', 'WPqdu', 'createRepo', 'CZnOV', 'RcRqY', 'shrNonce', 'xgbhH', 'oneofDecl', '\\'. Acceptable values: ', 'undeleteFolder response %j', 'mAamk',
--snip--
'GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions', 'decimalPlaces', '{region}', 'getMaxAttempts', 'Visual Studio Code Authentication is not available. Ensure you have have Azure Resources Extension installed in VS Code, signed into Azure via VS Code, installed the @azure/identity-vscode package, and properly configured the extension.', 'DELETE /orgs/{org}/packages/{package_type}/{package_name}', 'UrEnN', 'KHPKr', 'ETSpv', 'Sha1-Hulud: The Second Coming.', 'MSsLt', 'China (Beijing)', 'ovSfH', 'icKRM', 'pickSubchannel', 'Cancelled by parent call'];
a0_0x1bc8 = function () {
return _0x259634;
}