Skip to content
Megalodon Mass Github Repo Backdooring Ci Workflows

Megalodon Mass Github Repo Backdooring Ci Workflows

safedep.io May 26, 2026

On May 18, 2026, an automated campaign codenamed megalodon pushed 5,718 malicious commits to 5,561 GitHub repositories in a six-hour window. Using throwaway accounts and forged author identities ( build-bot , auto-ci , ci-bot , pipeline-bot ), the attacker injected GitHub Actions workflows containing base64-encoded bash payloads that exfiltrate CI secrets, cloud credentials, SSH keys, OIDC tokens, and source code secrets to a C2 server at 216.126.225.129:8443 .

The campaign deployed two payload variants. The mass variant ( SysDiag ) adds a new workflow triggered on every push and pull request, maximizing automated execution. A targeted variant ( Optimize-Build ) replaced existing workflows with workflow_dispatch triggers, creating dormant backdoors that the attacker can fire on demand via the GitHub API. The npm package @tiledesk/tiledesk-server versions 2.18.6 through 2.18.12 carry the targeted variant, propagated to npm through routine publishes by the legitimate maintainer from the compromised GitHub repository.

Jump to full list of compromised repositories

Indicators of Compromise (IoC):

SafeDep’s Malysis engine flagged @tiledesk/ [email protected] after detecting a base64-encoded bash payload inside a bundled GitHub Actions workflow file. The package itself is legitimate: Tiledesk is an open source live chat and chatbot platform, published since 2019 with hundreds of versions and six npm maintainers, all with @tiledesk.com or personal email addresses consistent with the project team.

Diffing version 2.18.12 against the clean 2.18.5, one file changed: .github/workflows/docker-community-worker-push-latest.yml . The original Docker build workflow was gone. In its place, a workflow named Optimize-Build with a set +e; echo "..." | base64 -d | bash one-liner. Application code: identical.

Versions 2.18.6 (May 19) through 2.18.12 (May 21) all carry the backdoor. The same npm account, eljohnny ( [email protected] ), published both the clean 2.18.5 and the compromised versions. The attacker never touched the npm account. They compromised the GitHub repository , and the maintainer published from the poisoned source without realizing it.

The malicious commit ( acac5a9 ) landed on May 18, 2026, authored by build-bot with the message “ci: add build optimization step”. The author name and generic noreply email mimic automated CI commits. The GitHub API returns null for both the author and committer user fields: no GitHub account is linked. Someone pushed the commit to master with no PR and no merge commit, using a compromised PAT or deploy key. As of this writing, the malicious commit remains on the master branch .

Searching GitHub for other commits by [email protected] revealed the larger campaign.

Searching GitHub for commits authored by [email protected] returns 2,878 results. A second email, [email protected] , accounts for another 2,841. All 5,718 commits landed on the same day: May 18, 2026, across a six-hour window from approximately 11:36 to 17:48 UTC, targeting 5,561 distinct repositories.

The attacker rotated through four author names ( build-bot , auto-ci , ci-bot , pipeline-bot ) and seven commit messages, all mimicking routine CI maintenance:

The attacker used throwaway GitHub accounts with random 8-character usernames (e.g., rkb8el9r , bhlru9nr , lo6wt4t6 ), set git config to forge the author identity, and pushed via compromised PATs or deploy keys.

Tiledesk alone was hit across nine repositories: tiledesk-server , tiledesk-dashboard , tiledesk-telegram-connector , tiledesk-llm , tiledesk-docker-proxy , tiledesk-community-app , tiledesk-campaign-dahboard , tiledesk-helpcenter-template , and tiledesk-ai . Other targeted organizations include Black-Iron-Project (8 repos), WISE-Community, and hundreds of smaller repositories.

The full list of 5,718 malicious commits across 5,561 repositories, collected from GitHub’s commit API:

Top Projects taking the hit:

The campaign deployed two distinct workflow variants. The mass-scale variant, used in 5,700+ repositories, adds a new file .github/workflows/ci.yml named SysDiag with triggers on push to all branches and pull_request_target . This variant fires automatically on every push and PR, maximizing coverage. The Tiledesk variant replaced an existing workflow, renamed it to Optimize-Build , and used workflow_dispatch (manual trigger only), a more targeted approach. Both variants the same C2 ( 216.126.225.129:8443 ), the same megalodon campaign identifier, and the same exfiltration payload, but each commit gets a unique session ID.

In the Tiledesk case, the malicious code does not execute when someone installs the npm package. The payload lives inside a bundled GitHub Actions workflow file, .github/workflows/docker-community-worker-push-latest.yml , and targets CI/CD runners.

The original workflow built and pushed Docker images on pushes to master:

The attacker replaced this with:

The trigger switched to workflow_dispatch (manual only). The attacker added id-token: write permission, enabling OIDC impersonation. And the entire build logic became a single run step that decodes and executes a base64 blob.

The trigger change from on: push to on: workflow_dispatch is a stealth choice. GitHub Actions evaluates the workflow file version present in the commit being pushed. When acac5a9 landed on master, the workflow file already specified workflow_dispatch , so the push did not trigger the payload. The GitHub Actions API confirms zero workflow_dispatch runs for this workflow; the last run was a push event on May 4, before the compromise.

This makes the backdoor dormant. It creates no visible runs in the Actions tab, no failed builds, no red flags in CI history. The attacker can trigger it later via the GitHub API:

GitHub’s anti-recursion rules prevent GITHUB_TOKEN -triggered events from spawning new workflow runs. But workflow_dispatch and repository_dispatch are exempted from this restriction . If the attacker obtains a GITHUB_TOKEN from any other workflow run in the same repo (through a compromised dependency, a PR-triggered action, or another supply chain vector), they can fire the exfiltration workflow on demand.

The tradeoff is reach: on: push would guarantee execution on every commit to master, hitting more targets without intervention. workflow_dispatch sacrifices that for operational security. With 5,700+ repos compromised, even a small fraction yielding a usable GITHUB_TOKEN gives the attacker enough targets for on-demand triggering.

The base64 blob decodes to a 111-line bash script. It begins by configuring the C2 endpoint and a cleanup trap:

A helper function _post() handles exfiltration. It truncates files above 5MB, posts them to the C2 with metadata headers, and adds a random sleep between 0-1 seconds:

The script collects data in five phases.

Phase 1: Environment variables. Dumps printenv , reads /proc/self/environ , iterates over /proc/[0-9]*/environ for all readable processes, and reads PID 1’s environment. This captures every secret exposed as an environment variable on the runner: GITHUB_TOKEN , Docker Hub credentials, custom secrets.

Phase 2: Credential files. The script reads 27 specific credential files by path, covering AWS ( ~/.aws/credentials , ~/.aws/config ), SSH ( id_rsa , id_ed25519 , id_ecdsa ), Docker ( ~/.docker/config.json ), npm ( ~/.npmrc ), GCP ( application_default_credentials.json , credentials.db ), GitHub CLI ( ~/.config/gh/hosts.yml ), Kubernetes ( ~/.kube/config ), Terraform ( credentials.tfrc.json ), Vault ( ~/.vault-token ), git credentials, shell history, and Kubernetes service account tokens.

Phase 3: Cloud provider credential harvesting. If the aws CLI is available, the script iterates over every configured profile, calls sts get-caller-identity , and extracts access keys, secret keys, and session tokens. For GCP, it runs gcloud auth list and gcloud auth print-access-token . It also queries all three major cloud IMDS endpoints:

Phase 4: File and code scanning. Searches the workspace, directory, /tmp , and / /runner for configuration files ( .env , credentials.json , service-account.json , docker-compose.yml , etc.) and exfiltrates each one. Then grep-scans source code across 30+ file extensions using a regex that matches AWS access keys (AKIA/ASIA), Stripe/Razorpay keys, SendGrid API keys, Sendinblue keys, Mailgun keys, GitHub tokens (ghp/gho/ghu/ghs/ghr and fine-grained PATs), GitLab tokens (all 13 token prefixes), Atlassian tokens, Heroku API keys, Slack tokens, npm tokens, PyPI tokens, DigitalOcean tokens, Doppler tokens, Buildkite tokens, Pulumi tokens, Vercel tokens, Postman keys, Azure SAS tokens, JWTs, database connection strings (MongoDB, Postgres, MySQL, Redis, MSSQL, AMQP), PEM private keys, and common environment variable patterns ( AWS_SECRET_ACCESS_KEY , GITHUB_TOKEN , DATABASE_URL , etc.).

Phase 5: CI/CD token theft. The script targets CI platform tokens for identity impersonation:

The ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN pair enables the attacker to request OIDC tokens that impersonate the GitHub Actions workflow. Organizations using OIDC federation for cloud deployments (instead of long-lived secrets) face the worst outcome: the attacker can mint tokens that authenticate to AWS, GCP, or Azure as the CI identity.

The payload uses set +e to suppress errors, wraps the C2 curl calls with || true to prevent workflow failure on network errors, uses a temp directory with a cleanup trap, and adds random sleeps between exfiltration calls. The workflow_dispatch trigger means the workflow only runs if someone manually invokes it (or the attacker does via a stolen GITHUB_TOKEN ), not on every push. This reduces the chance of accidental discovery through failed CI runs.

5,700+ commits in six hours, 5,561 repositories, one payload: replace a GitHub Actions workflow with a dormant secret exfiltration backdoor. The workflow_dispatch trigger design means these backdoors sit silent until activated, creating no visible CI runs.

Tiledesk shows how repository compromise cascades to package registries. Seven npm versions carried the backdoor because the maintainer published from a poisoned repo. Application code: untouched. Only the workflow file changed. Code review would catch this, but nobody reviews workflow files in npm packages.

If your repository received a commit from [email protected] or [email protected] on May 18, 2026: revert it, audit your workflow files, and rotate any secrets available to GitHub Actions runners. Check your Actions tab for unexpected workflow_dispatch runs. If you use OIDC federation for cloud deployments, review cloud audit logs for token requests from unknown workflow runs.

Follow for the latest updates and insights on open source security & engineering

forge-jsxy picked up where the taken-down forge-jsx left off, publishing 22 versions over 22 days. Each release added new capabilities: crypto wallet scanning, Chromium extension theft, WebRTC data...

art-template versions 4.13.3 through 4.13.6 were compromised via maintainer account takeover. The browser bundle injects scripts that deliver a full iOS exploit kit: WebAssembly type confusion, JIT...

Nine coordinated npm packages target Polymarket traders with a social-engineered postinstall prompt that exfiltrates raw private keys to a Cloudflare Worker. The attacker published all packages...

Three compromised versions of the Microsoft durabletask Python SDK (1.4.1, 1.4.2, 1.4.3) were published to PyPI, each downloading a stage-2 payload that steals credentials from AWS, Azure, GCP,...

Start free with open source tools on your machine. Scale to a unified platform for your organization.