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.

15 points | by antihero 11 hours ago

4 comments

  • gionn 9 hours ago
    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...
  • antihero 8 hours ago
    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.
    • nhecker 7 hours ago
      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.

      • antihero 3 hours ago
        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.

      • thejaybird 5 hours ago
        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)

    • nhecker 5 hours ago
      https://safedep.io/miasma-worm-ai-coding-agent-config-inject... seems to describe the same symptoms, for what it's worth.
  • thejaybird 5 hours ago
    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

  • nikita2206 9 hours ago
    Did you dig anything on where it came from for you? Some NPM package?
    • antihero 8 hours ago
      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.