Supply chain attack alert: .github/setup.js
Our org GitHub just got compromised massively by a supply-chain attack. Vectors are
* Claude hooks
* Gemini hooks
* Cursor setup
* VScode tasks
It adds all of the above to execute node .github/setup.js, an obfuscated file.
Check infected: `rg --hidden --no-ignore 'node .github/setup.js`
It spreads by adding mimic'd skip-ci commits to open PRs which then get merged.
Payload is obfuscated, available on request.
If this is already a known one in the world, apologies, it hit us at around 10PM BST last night, the damage would have been incredible.
Still trying to identify the original source.
OSS maintainer, @icflorescu on GitHub, owner of a few affected repos here.
My full analysis and IOCs: https://dev.to/icflorescu/the-bot-that-never-was-2mfp (source repo on codeberg: https://codeberg.org/icflorescu/miasma-github-incident)
Made a quick script to find affected repos/branches and optionally wipe the commits which contains malware: https://github.com/gionn/malware-cleanup/blob/master/scanner...
Thanks a lot, Gionn, for also scanning and raising issues on this matter!
always glad to help!
Attack is called "Hades - The End for the Damned", it exfiltrates secrets including ALL ORG GITHUB ACTIONS SECRETS via creating compromised actions, through GitHub public repos with encrypted payloads.
If you're saying it only impacts public repos, I don't think that's quite right. It appears to impact private ones as well. Source: first-hand experience. If you're claiming that the only export vector is via public repos then I can't refute that. But just trying to clarify here.
And after a quick glance I'm not seeing any correlation between "Hades - The End for the Dammed" and this worm; would also love a source for this claim.
Take the JS file and decode it!
Decoded execution chain ----------------------- 1. Outer layer: - Starts with try{eval(function(s,n){...})([large numeric array].map(...).join(""),1)) - Converts numeric character codes into a string. - Applies a Caesar +1 shift to alphabetic characters. - eval() executes the decoded layer.
2. First decoded layer: - Imports node:crypto. - Defines an AES-128-GCM decryptor. - Decrypts two embedded payloads: a) _b: small Bun bootstrap/loader b) _p: large obfuscated payload (~686 KB) - Writes _p to a temporary JS file under /tmp/p<random>.js. - If Bun is available, runs: bun run "<temp file>" - If Bun is unavailable, downloads Bun from GitHub releases and then runs the payload.
3. Small loader payload: - Downloads Bun v1.3.13 from: https://github.com/oven-sh/bun/releases/download/bun-v1.3.13... - Uses curl and unzip. - Creates temporary directories under /tmp/b-* - Runs the large payload using Bun.
4. Large payload: - Obfuscated JavaScript with a string-table decoder and a second custom encrypted string layer. - After decoding strings, the payload clearly contains credential and secret collection logic.
Observed behaviour / capabilities --------------------------------- The payload appears to collect or search for: - GitHub tokens / PATs / GitHub Actions OIDC tokens - npm tokens and npm OIDC package exchange tokens - RubyGems API keys - AWS credentials, STS metadata credentials and Secrets Manager secrets - Azure credentials, service principals and Key Vault secrets - GCP service account tokens and Secret Manager secrets - Vault tokens and Vault secrets - Kubernetes service account tokens and kubeconfig - Docker credentials - SSH keys and config - Git credentials - .env files and common project secrets - Claude configuration and project files - OnePassword items via the op CLI - Slack, Discord, Signal, Telegram and Element local data - Crypto wallet files such as Exodus, Ledger Live, Ethereum keystores and Monero data - Shell and database history files
Exfiltration / persistence style -------------------------------- The payload contains logic to: - Create or use GitHub repositories through GitHub API endpoints. - Commit collected data/content into repository files. - Add/update files such as: - .vscode/tasks.json - .claude/index.js - .claude/settings.json - .claude/setup.mjs - .vscode/setup.mjs - Use commit messages such as: - chore: update dependencies - create del-commit: - Create a GitHub repo with description: - Hades - The End for the Damned - Use api.anthropic.com with path v1/api as an apparent outbound endpoint. - Use a token/string: - IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully
Notable URLs / endpoints ------------------------ - https://api.github.com - https://api.github.com/graphql - https://github.com/ - https://github.com/actions/runner - https://github.com/oven-sh/bun/releases/download/bun-v1.3.13... - https://registry.npmjs.org/ - https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/pack... - https://registry.npmjs.org/-/npm/v1/tokens - https://registry.npmjs.org/-/whoami - https://rubygems.org/api/v1/api_key.json - https://rubygems.org/api/v1/gems - https://cloudresourcemanager.googleapis.com/v1 - https://secretmanager.googleapis.com/v1 - https://graph.microsoft.com/v1.0/me - https://login.microsoftonline.com/ - https://vault.azure.net/.default - http://169.254.169.254/latest/api/token - http://169.254.169.254/latest/meta-data/iam/security-credent... - http://169.254.170.2 - http://127.0.0.1:8200 - api.anthropic.com / v1/api
Files/globs targeted -------------------- - */.env - */.env.local - */.env.production - */.git/config - */config/database.yml - */wp-config.php - .env - .git-credentials - .npmrc - ~/.npmrc - ~/.pypirc - ~/.yarnrc - ~/.aws/config - ~/.aws/credentials - ~/.azure/accessTokens.json - ~/.azure/msal_token_cache.* - ~/.config/gcloud/access_tokens.db - ~/.config/gcloud/application_default_credentials.json - ~/.config/gcloud/credentials.db - ~/.docker/config.json - ~/.docker//config.json - /root/.docker/config.json - ~/.kube/config - /etc/rancher/k3s/k3s.yaml - /var/run/secrets/kubernetes.io/serviceaccount/token - ~/.terraform.d/credentials.tfrc.json - ~/.ssh/id - ~/.ssh/id_rsa - ~/.ssh/id_ed25519 - ~/.ssh/id_ecdsa - ~/.ssh/config - ~/.ssh/known_hosts - ~/.gitconfig - ~/.config/git/credentials - ~/.netrc - ~/.bash_history - ~/.zsh_history - ~/.python_history - ~/.psql_history - ~/.mysql_history - ~/.claude.json - ~/.claude/* - ~/.claude/projects/* - ~/.claude/mcp.json - %USERPROFILE%\.claude.json - %USERPROFILE%\.claude* - ~/.config/Slack/Cookies - ~/.config/discord/Local Storage/leveldb/* - ~/.config/Signal/* - ~/.local/share/TelegramDesktop/tdata/* - ~/.config/Element/Local Storage/* - ~/.config/Exodus/exodus.wallet/* - ~/.config/Ledger Live/* - ~/.ethereum/keystore/* - ~/.monero/* - ~/.local/share/keyrings/.keyring - ~/.kde/share/apps/kwallet/.kwl - ~/.config/filezilla/sitemanager.xml - ~/.config/filezilla/recentservers.xml
Environment variables checked ----------------------------- - GITHUB_ACTIONS - GITHUB_REPOSITORY - GITHUB_REF - ACTIONS_ID_TOKEN_REQUEST_URL - ACTIONS_ID_TOKEN_REQUEST_TOKEN - TARGET_REPOS - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN - AWS_PROFILE - AWS_REGION - AWS_DEFAULT_REGION - AWS_WEB_IDENTITY_TOKEN_FILE - AWS_CONTAINER_CREDENTIALS_RELATIVE_URI - AWS_CONTAINER_CREDENTIALS_FULL_URI - AWS_CONTAINER_AUTHORIZATION_TOKEN - AZURE_TENANT_ID - AZURE_CLIENT_ID - AZURE_CLIENT_SECRET - AZURE_FEDERATED_TOKEN_FILE - AZURE_KEY_VAULT_NAME - GOOGLE_APPLICATION_CREDENTIALS - GOOGLE_CLOUD_PROJECT - GCP_PROJECT - GCLOUD_PROJECT - KUBECONFIG - VAULT_ADDR - VAULT_API_TOKEN - VAULT_TOKEN_FILE - VAULT_TOKEN_PATH - VAULT_AWS_ROLE - VAULT_ROLE - HOME - USERPROFILE - LANG / LANGUAGE / LC_ALL / LC_MESSAGES (edited)
Nope, the public repos are what the on-machine payload creates. Sorry, I worded that wrong, I meant it exfiltrates to.
The main attack is using compromised repo keys to:
* Create malicious actions to JSON dump and exfiltrate all GitHub org secrets.
* Commit the payload delivering hooks/scripts to any repo/PR it has access to.
* Mimics previous commits/timestamps, however you can see the key that did it by seeing the push in activity/audit logs.
https://safedep.io/miasma-worm-ai-coding-agent-config-inject... seems to describe the same symptoms, for what it's worth.
For me i feel the attack vector is
Public repo > infect by merge > github runner picks up and gets infected > and github action (from a repo) that then runs on runner getw effected
FYI we released a discovery and mitigation tool today: https://github.com/Team-Rockstars-Security/antimiasma
Did you dig anything on where it came from for you? Some NPM package?
We're trying to figure this out, we've nailed it to the developer machine but not sure how that machine got infected. Possibly through GHA.