{"slug": "inside-the-miasma-software-supply-chain-attack-toolkit", "title": "Inside the Miasma Software Supply Chain Attack Toolkit", "summary": "The Miasma software supply chain attack toolkit has been released as open source across multiple GitHub repositories, likely through compromised developer accounts. The toolkit enables attackers to execute supply chain attacks against public registries including PyPI, npm, and RubyGems, as well as GitHub repositories, GitHub Actions, and AI coding tools through stolen credentials. The codebase includes capabilities to bypass GitHub environment protection rules, generate valid Sigstore provenance bundles for trojanized packages, and operate three independent command-and-control channels using GitHub commit search.", "body_md": "# Inside the Miasma Software Supply Chain Attack Toolkit\n\n### Table of Contents\n\nThe infamous [Miasma worm](/miasma-worm-ai-coding-agent-config-injection) goes open source. Multiple GitHub repositories with name `Miasma-Open-Source-Release`\n\nstarted appearing since yesterday. Most of them are likely published through compromised developer accounts. We have seen this in the past when [Team PCP](/ti/campaigns/teampcp) open sourced the Mini Shai-Hulud payload which in turn, likely motivated further software supply chain attacks.\n\nWe managed to obtain the source code from [one such repository](https://github.com/YangYongAn/Miasma-Open-Source-Release) (yanked now). As the developers of [PMG](https://github.com/safedep/pmg), we are continuously looking to update our benchmark of attacker TTPs against which we evaluate PMG, especially its [sandbox features](https://github.com/safedep/pmg/blob/main/docs/sandbox.md).\n\nIn this blog, we do a deep-dive analysis of the `Miasma-Open-Source-Release`\n\nsource code obtained from one of the public GitHub repositories.\n\n## TL;DR\n\nThe Miasma codebase appears to be larger than a supply chain worm. It is a full supply chain attack toolkit that allows the operator to execute various attacks via stolen credentials against arbitrary or targeted packages on public registries (PyPI, npm, RubyGems), JFrog Artifactory, GitHub repositories and GitHub Actions, AI coding tools config poisoning, SSH based lateral movement and other attack vectors.\n\nSome of the interesting findings from the analysis:\n\n- Bypasses GitHub environment protection rules to trigger deployments.\n[Details](#npm-oidc-branch-mutator) - Generates valid Sigstore provenance bundles for trojanized npm packages.\n[Details](#npm-oidc-mutator) - Three independent C2 channels using GitHub commit search, each with a different search string and crypto key.\n[Details](#command-and-control) - Dead-man switch that wipes the victim’s home directory if the stolen PAT is revoked.\n[Details](#dead-man-switch) - Victim PATs embedded in exfiltration commits create a self-perpetuating flywheel for future worm instances.\n[Details](#exfiltration) - Hijacks GitHub Actions semver tags via orphan commits with cloned author metadata.\n[Details](#github-actions-mutator) - Injects into 13 AI coding tools (Claude, Gemini, Cursor, Copilot, Kiro, Cline, and others).\n[Details](#slow-path-propagation) - Living off the pull request (LOTP) technique injects payload into existing project files across 12+ languages.\n[Details](#living-off-the-pull-request-lotp) - Credential harvesting from AWS, Azure, GCP, Kubernetes, HashiCorp Vault, and password managers (1Password, Bitwarden).\n[Details](#credential-gathering) - Dumps GitHub Actions runner memory via\n`/proc`\n\nto extract secrets not exposed as env vars.[Details](#fast-path) - 5-layer build obfuscation with per-build random keys, making each compiled payload unique.\n[Details](#obfuscation-and-evasion) - Targets npm, PyPI, and RubyGems via both stolen auth tokens (fast path) and OIDC trusted publishing (slow path).\n[Details](#propagation) - MCP-suffixed typosquatting mode for PyPI packages.\n[Details](#typo-mutator)\n\n### GitHub as a Common and Control Infrastructure\n\nWe have been tracking [TeamPCP, Mini Shai-Hulu, Miasma and other related campaigns](/ti). One of the common observation is, attackers are moving away from custom C2 infrastructure which requires maintenance, warming and safeguarding. Instead, they are now leveraging GitHub as a full-fledged C2 infra for remote command execution, configuration, exfiltration. This is a key behavioural shift because, traditional network based detection and protection tools rely on baselining and anomaly detection. Defenders now have to operate closer to application protocol to identify behavioural anomaly instead of network based anomalies.\n\n## High Level Architecture\n\nThe repository consists of the following files:\n\nThe file listing indicates the following:\n\n[Bun](https://bun.sh)as a dependency for the payload, consistent with[droppers that we have seen in the past](/redhat-cloud-services-hit-by-mini-shai-hulud-npm-worm)`ARCHITECTURE.MD`\n\nand`INTEGRATION_TESTING.md`\n\nfiles indicate AI coding agent generated and maintained codebase.\n\n### ARCHITECTURE.MD\n\nThe `ARCHITECTURE.md`\n\ncalls out the intention of the worm:\n\nA worm that aims to automate spreading across multiple developer tooling ecosystems. Written in TypeScript, executed via Bun, designed for CI/CD environments (especially GitHub Actions) and developer machines. Exfiltrates secrets and propagates through NPM packages, PyPI wheels, RubyGems, GitHub repositories and Actions, Claude settings hooks, SSH, and AWS SSM.\n\nThe same file calls out a key architecture decision that aligns with what we have identified in past campaigns and why we consider network baselining an ineffective detection strategy for such payloads:\n\nRequires NO C2 infrastructure. No dealing with takedowns or maintaining infrasturcture. Stolen GitHub PATs are all that is necessary.\n\nThe same file also calls out the following gaps in the current implementation:\n\n[PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/)based spreading is untested.- SSH based propagation through\n`scp`\n\nand`ssh`\n\n(exec) is untested. - JFrog Artifactory\n`npm`\n\npackage infection is untested. [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/)based package infection is untested.- GCP and Azure provider for secret exfiltration does not work.\n\n### Components\n\nAt a high level, the codebase consists of:\n\n`scripts/`\n\n- Containing scripts for payload preparation, obfuscation and operations.`src/`\n\n- Contains the actual worm source code with`src/index.ts`\n\nas the entrypoint.\n\nThe worm (application) in turn is divided into following modules:\n\n- Orchestrator\n- Assets - Pre-crafted files used at runtime such as loaders, malicious GitHub Actions workflow, VSCode settings for code execution etc.\n- Collector\n- Dispatcher\n- Mutator\n- Provider\n- Sender\n- Utils\n\nThe architecture also indicates what we have seen in Shai-Hulud class of worms that consist of a fast and slow path for credential exfiltration and propagation. The fast path is used for quick exfiltration of credentials and slow path is used for propagation through various ecosystems which may take time to execute.\n\nFollowing is the architecture diagram from the `ARCHITECTURE.md`\n\nfile:\n\n## Application Logic\n\n### Preflight\n\nThe pre-flight stage of the worm execution performs the following checks:\n\n- Check for known sandbox environment and fail fast.\n- Checks and ensures proxy env vars are set consistently across the different variables required by\n`bun`\n\nvia`libcurl`\n\n. - Bail out on Russian systems identified through locale resolution.\n\nInterestingly, the worm has targeted mode for npm OIDC based self-propagation. During the pre-flight, it checks if `WORKFLOW_ID`\n\nand `REPO_ID_SUFFIX`\n\nenv-var is set, matches (substring) `GITHUB_WORKFLOW_REF`\n\nand `GITHUB_REPOSITORY`\n\nenv-vars, then it executes the npm OIDC based propagation flow and quits without executing the full worm logic.\n\nThe pre-flight also detects malware analysis environments through known fake environment variables, docker containers, `sudo`\n\nrestrictions on GitHub Actions, and known security tools by process name and file path. It actively restores `sudo`\n\naccess using docker privileged containers and overwrites `/etc/resolv.conf`\n\nwith public DNS to evade dynamic analysis tools. Example code for `sudo`\n\nrestoration:\n\n### Fast Path\n\nIf the pre-flight checks are successful, the worm executes its fast path, that executes:\n\n- Credential gathering from known sources via\n`gatherQuickResults()`\n\n. - Infect npm, PyPI, RubyGem packages via download, patch, publish vector if a valid token is found.\n- Create a layered sender chain containing the transport logic for exfiltrated data.\n- Execute the collector to validate, cache (in-memory) quick results and deliver via the sender chain.\n\nThe fast path is designed to quickly exfiltrate credentials and other sensitive information before dropping the larger propagation and persistence payloads, that may be detected by EDR and other security tools.\n\n### Credential Gathering\n\nThe worm uses *Provider* as an abstraction for source specific credential gathering logic. Following providers are registered:\n\nThe full provider bundle also appends services that are deemed to be available based on credentials gathered from fast path harvesting, such as `GitHubActionsService`\n\n, `GrepProvider`\n\n. Interestingly, the worm logic uses `GrepProvider`\n\nwhen classic GitHub tokens are found. The following code determines when to register the `GitHubActionsService`\n\n:\n\n### Credential Collection\n\nThe `Collector`\n\nacts as an orchestrator for classifying credentials, taking action based on credential type, and triggering exfiltration. The high level behavior is as follows:\n\n- In-memory, bounded cache for\n`ProviderResult`\n\n(credentials), flushed based on byte count. - Classify token type by pattern matching and dispatch token-specific handlers that perform validation and propagation using the token’s privileges.\n- Execute the slow path for credential collection, which includes cloud services, password managers, vaults, and more.\n\nExample code for dispatching token handles by token pattern:\n\nWhen collector’s in-memory buffer (cache) reaches a threshold or when the `finalize`\n\nmethod is called, it invokes the `Dispatcher`\n\ncomponent to exfiltrate the collected credentials from the buffer.\n\n### Exfiltration\n\nExfiltration of collected credentials is handled by `Dispatcher`\n\nand `Sender`\n\ncomponents. The dispatcher is responsible for:\n\n- Encrypting the batch of credentials to be exfiltrated\n- Exfiltrate by invoking the\n`Sender`\n\nchain\n\nAt the time of initialization in `src/index.ts`\n\n, the worm creates the sender chain by invoking:\n\nThe `buildSenderChain`\n\nitself is dynamic and creates the senders based on credentials collected in the fast path. Specifically, it creates the primary GitHub sender if it can find an attacker controlled PAT by searching for commits using [GitHub Commit Search API](https://docs.github.com/en/rest/search/search#search-commits).\n\nIn `src/github_utils/fetcher.ts`\n\n, it attempts to find this attacker controlled PAT by searching for the string `DontRevokeOrItGoesBoom`\n\n. The attacker pre-plants GitHub PATs by committing them to public repos. The commit message format is:\n\nThe payload for the attacker’s PAT is encrypted with a hardcoded AES key from `src/utils/config.ts`\n\n:\n\nOnce decrypted and validated with a prefix `github_pat_11A`\n\n, it checks that the token has `repo`\n\nscope and has 200+ rate limit quota remaining, before it selects it for use else continue with the next. This approach allows the attackers to continuously update the worm with new PATs, likely leveraging compromised credentials and developer machines, almost like a fly-wheel.\n\nIt then creates the primary GitHub sender using the attacker controlled PAT:\n\nIf no attacker controlled PAT is found, it creates the GitHub sender using victim’s own PAT found earlier in the fast path. This behaviour is again consistent with the observed worm behaviour of exfiltrating encrypted credentials batch using public repositories created under victim’s account.\n\nWhen using a victim’s PAT, the sender embeds the PAT in the exfiltration commit message. The token is AES-256-CBC encrypted and disguised as a `github_pat_11A...`\n\nstring, then included in the commit message as `DontRevokeOrItGoesBoom:<encrypted-token>`\n\n. This closes the flywheel loop. Future worm instances searching for `\"DontRevokeOrItGoesBoom\"`\n\nfind the victim’s PAT and use it as an exfiltration token, turning every compromised developer account into infrastructure for the next infection. The same embedded token also triggers the [dead-man switch](#dead-man-switch) installation, which wipes the victim’s home directory if the token is revoked.\n\n`DomainSenderFactory`\n\nexists in the codebase and allows the worm to exfiltrate encrypted credentials to attacker-controlled C2 infrastructure. However, it is not wired into the current sender chain. This likely indicates the attackers are relying on public services only for exfiltration, avoiding detection by network monitoring tools and C2 takedowns.\n\nAll credential batches are encrypted using a generated AES key and IV. The AES key is in turn encrypted using the attacker’s RSA-4096 public key. Encryption is performed once by the first registered *Sender* and the resulting envelope is reused across fallback attempts. The encryption envelope format is consistent with past Shai-Hulud and Miasma campaigns.\n\n### Propagation\n\nThere are two types of propagation approach used by the worm:\n\n- Fast path propagation by publishing a new version of accessible package with worm payload injected into it.\n- Slow path propagation (mutation) by leveraging OIDC publisher trust between GitHub Actions and upstream registries like npm, PyPI, RubyGems and other mechanisms such as SSH, AWS SSM and other supported vectors.\n\n#### Fast Path Propagation\n\nThe fast path propagation consists of registry infection via stolen credentials. For example, the `npm`\n\nregistry propagation uses the following logic:\n\n- Find writable packages via the registry API\n- For each writable package, fetch metadata and get the latest dist-tag tarball URL\n- Download each package, extract, and inject its own obfuscated payload\n- Inject a\n`binding.gyp`\n\nfile in the package root with the following JSON\n\n- Bump the patch version of the package\n- Add\n`bun`\n\nas a dependency since it is required to run the worm - Repack into\n`.tgz`\n\nand publish to the registry using discovered credentials\n\nThe npm registry client has the capability to compute and attach a Sigstore provenance bundle using a short-lived credential obtained from Sigstore via GitHub OIDC (workload identity). However, this requires an OIDC token, not a stolen npm auth token. The worm executes the registry infection in the fast path regardless of whether a GitHub OIDC token is available in the environment.\n\n#### Slow Path Propagation\n\nThe worm uses multiple *Mutator* in the slow path to replicate. This includes:\n\n`NPMOidcClient`\n\n- Direct OIDC npm publish`NpmOidcBranchMutator`\n\n- Indirect OIDC based publishing via dangling commits`ActionMutator`\n\n- GitHub actions tag hijacking`RepositoryMutator`\n\n- Pull request feature branch poisoning`Claude`\n\n- AI coding tools config poisoning. See[Config Files that Run Code](/config-files-that-run-code)for more details on this attack vector.`SshMutator`\n\n- Lateral movement using SSH credentials,`scp`\n\nand`ssh`\n\nbased remote exec`ReadmeUpdater`\n\n- AI coding tools config poisoning on remote branches accessible using discovered PATs\n\n##### NPM OIDC Mutator\n\n`NPMOidcClient`\n\nruns in GitHub Actions, requests OIDC token from `ACTIONS_ID_TOKEN_REQUEST_URL`\n\nusing runner’s workload identity, with `audience=npm:registry.npmjs.org`\n\nand performs the same fast path infection technique with additional capability of generating and attaching a Sigstore provenance bundle.\n\n##### NPM OIDC Branch Mutator\n\n`NpmOidcBranchMutator`\n\nis the indirect variant. It does not require execution inside GitHub Actions. Instead, it uses a stolen GitHub PAT to find repositories that publish to npm via OIDC trusted publishing and tricks them into running a trojanized workflow on the next CI trigger.\n\nTarget repo detection in `src/mutator/npmoidc/detector.ts`\n\nchecks three conditions:\n\n- A non-private\n`package.json`\n\nwith a`name`\n\nfield - A workflow YAML with a publish step (\n`npm publish`\n\n,`yarn publish`\n\n, or`pnpm publish`\n\n) `id-token: write`\n\npermission on the workflow job\n\nMonorepo layouts are supported by scanning `packages/`\n\n, `libs/`\n\n, `apps/`\n\n, `plugins/`\n\nfor additional `package.json`\n\nfiles.\n\nThe mutator has two code paths depending on token scope:\n\n**With**: pushes an orphan commit with the modified workflow YAML and payload to a new`workflow`\n\nscope`snapshot-<random>`\n\nbranch. The push event triggers the workflow natively.**Without**: uses a dangling commit technique. Commit 1 (child of default branch HEAD) adds the trojanized workflow. Commit 2 (child of commit 1) restores the original tree. A branch is created at commit 2, so the tip looks clean. A GitHub deployment targeting commit 1 triggers the workflow, and the deployment record is deleted afterward.`workflow`\n\nscope\n\nIf the target workflow uses GitHub environment protection and the token has admin access, the mutator bypasses those restrictions before creating the deployment. The bypass handles three types of environment protection:\n\n- Required reviewers. Cleared by sending an empty\n`reviewers: []`\n\nto the environment API. - Protected branches policy. Cleared so the temporary\n`snapshot-*`\n\nbranch is allowed to deploy. - Custom branch policies. Checked for pattern match first, cleared if the branch name does not match.\n\nThe original branch policy config is saved for restoration, but the reviewer list is not. If the mutator runs against a repo with required reviewers, that protection is permanently removed.\n\n##### GitHub Actions Mutator\n\n`ActionMutator`\n\nhijacks custom GitHub Actions by force pushing trojanized orphan commits to their semver tags. Any downstream workflow referencing `uses: owner/action@v1`\n\npulls the compromised version.\n\n- Enumerates all public, non-fork repos the stolen PAT has push access to\n- Uses GraphQL to fetch\n`action.yml`\n\nor`action.yaml`\n\nfrom the repo root - Parses and classifies each action as composite, JavaScript (\n`using: node20`\n\n), or Docker - For composite actions, appends\n`setup-bun`\n\nand a payload execution step to the existing steps - For JavaScript and Docker actions, builds a new composite wrapper that delegates to the original action pinned at the real HEAD commit SHA, passes through all inputs, then appends the payload step\n\nTag hijacking creates a single Git tree containing the modified `action.yml`\n\nand `index.js`\n\n(self-extracting worm payload), then for each tag matching prefix `v`\n\n:\n\n- Resolves the tag to a commit (handles both lightweight and annotated tags)\n- Creates an orphan commit (no parents) with the payload tree, copying the original commit’s message, author, and committer metadata\n- Force-pushes the tag ref to point at the orphan commit\n\nThe orphan commit has no parents, so `git log`\n\non the tag shows only the single hijacked commit. The original history is disconnected. But because the commit metadata is cloned from the original, the hijack is harder to spot in casual inspection. The payload step uses `if: always()`\n\nto ensure execution even if the original action’s steps fail.\n\n##### GitHub Repository Mutator\n\n`RepositoryMutator`\n\npoisons feature branches on writable repos by pushing commits that contain the worm payload and AI tool config files. It targets branches backing open pull requests, where developers are likely to interact with the infected code soon.\n\n- Enumerates writable repos, fetches open PRs via GraphQL, extracts head branches\n- Up to 10 branches per repo in normal mode, 30 in aggressive mode\n- Skips enterprise-managed orgs and repos where\n`TARGET_REPOS`\n\nis set\n\nEach commit adds the following files to the branch:\n\n`.github/setup.js`\n\nwith the self-extracting worm payload`.claude/settings.json`\n\nwith a Claude Code SessionStart hook`.gemini/settings.json`\n\nwith a Gemini CLI SessionStart hook`.cursor/rules/setup.mdc`\n\nwith an`alwaysApply: true`\n\nrule instructing the AI to run the payload`.vscode/tasks.json`\n\nwith a task triggered on`folderOpen`\n\n- Optionally a modified existing source file via the “lotp” (living off the pull request) module\n\nThe mutator spoofs author by fetching the previous commit’s author info via GraphQL and reusing it on the malicious commit. It appends `skip-checks:true`\n\nto the commit message to suppress CI checks. If the token has admin access, it also attempts to commit directly to the default branch by temporarily removing branch protection rules.\n\nAggressive mode is triggered when exfiltration fails in `src/index.ts`\n\n. It widens the blast radius by including private repos and targeting 30 branches per repo instead of 10.\n\n#### Other Mutators\n\n##### Typo Mutator\n\n`TypoMutator`\n\nis an operator configured mode for PyPI typosquatting. It is not part of the automatic mutation plan. It triggers in the collector’s `handlePypiTokens()`\n\nwhen `TYPO_MODE=1`\n\nand `TARGET_PACKAGES`\n\nenv vars are set, as an alternative to the normal `PypiMutator`\n\nflow.\n\n- Takes a list of target package names from\n`TARGET_PACKAGES`\n\n- For each package, downloads the original wheel from PyPI\n- Injects the\n`.pth`\n\npayload into the wheel - Generates typosquat names and publishes under the first available name\n- Checks PyPI to skip names that are already taken\n- Aborts immediately on rate limiting (HTTP 429)\n\nThe typo name generation in `src/mutator/typo/typoGen.ts`\n\nappends MCP-related suffixes rather than using keyboard-adjacent substitution:\n\nThe comment in the source says “keyboard proximity” but the implementation only appends `-mcp`\n\nand `-mpc`\n\nsuffixes. This likely reflects that PyPI blocks direct typosquats of popular package names but allows suffixed variants. The `-mcp`\n\nsuffix is topical given the rise of MCP (Model Context Protocol) tooling.\n\n##### AWS SSM Mutator\n\n`SsmMutator`\n\npropagates to EC2 instances managed by AWS Systems Manager.\n\n- Resolves AWS credentials from the default chain and validates via\n`sts:GetCallerIdentity`\n\n- Calls\n`ssm:DescribeInstanceInformation`\n\nacross 17 AWS regions in parallel, filters for online instances - Base64-encodes the self-extracting payload, chunks it into 24KB pieces (SSM command size limit)\n- Delivers via\n`ssm:SendCommand`\n\nusing`AWS-RunShellScript`\n\n, reassembles on target via`printf`\n\nappends, decodes, executes with`node`\n\n, and cleans up - 5 instances concurrent, 120 second timeout per command\n\nThis is a lateral movement vector within AWS environments. If the worm runs on a machine with AWS credentials that have SSM access, it can spread to every managed EC2 instance across all regions.\n\n##### Living Off The Pull Request (LOTP)\n\nThe `RepositoryMutator`\n\nuses a “lotp” module (`src/mutator/repository/lotp.ts`\n\n) to inject the worm payload into existing project files that developers run as part of their normal workflow. It maps the repo’s primary language to a list of candidate files, checks which ones exist, and injects a language specific command into the first match.\n\nSupported injection targets:\n\n`package.json`\n\n(JS/TS): appends to the`test`\n\nscript`Makefile`\n\n(most languages): adds a phony target hooked into`all`\n\nor`.DEFAULT_GOAL`\n\n`tox.ini`\n\n(Python): appends to`[testenv] commands`\n\n`setup.py`\n\n/`.py`\n\nfiles: prepends`import os; os.system(...)`\n\n`Gemfile`\n\n/`Rakefile`\n\n/`Podfile`\n\n(Ruby): prepends`system(...)`\n\n`composer.json`\n\n(PHP): adds a`post-install-cmd`\n\nscript`Dockerfile`\n\n: injects a`RUN`\n\ninstruction`CMakeLists.txt`\n\n(C/C++): injects`execute_process()`\n\n- Shell scripts: injects after the shebang line\n\nThe fallback for repos with no recognized language is `Makefile`\n\nand `package.json`\n\n. The injected command runs the worm payload as a side effect of normal developer activity (`npm test`\n\n, `make`\n\n, `tox`\n\n, `pod install`\n\n, etc.).\n\n### Persistence\n\n`InstallMonitor`\n\ninstalls a persistent Python based C2 polling agent on the victim’s machine. It runs in the slow path mutation phase and is in the *always-run* set, executing on every worm invocation regardless of what tokens were found. It skips if EDR is detected or the machine has fewer than 4 CPU cores. The EDR check (`hasHostileEDR()`\n\nin `src/utils/config.ts`\n\n) scans for process names and file paths belonging to:\n\n- CrowdStrike Falcon (\n`falcon-sensor`\n\n,`falcond`\n\n,`csfalcon`\n\n,`crowdstrike`\n\n) - SentinelOne (\n`sentinelone`\n\n,`sentinelagent`\n\n) - Microsoft Defender for Endpoint (\n`mdatp`\n\n,`wdavdaemon`\n\n) - Carbon Black (\n`cbagent`\n\n,`cbdaemon`\n\n) - Cylance (\n`cylance`\n\n) - Trend Micro / Deep Security (\n`trendmicro`\n\n,`ds_agent`\n\n) - FireEye / Trellix (\n`xagt`\n\n) - osquery (\n`osquery`\n\n) - Tanium (\n`tanium`\n\n) - Qualys (\n`qualys`\n\n)\n\nThe installer (`INSTALL_MONITOR.sh`\n\n) copies the monitor script to `~/.local/share/updater/update.py`\n\nand registers it as a persistent service:\n\n- On Linux: a systemd user service with\n`loginctl enable-linger`\n\nso it survives logouts - On macOS: a LaunchAgent plist with\n`RunAtLoad`\n\nand hourly`StartInterval`\n\nThe monitor (`GITHUB_MONITOR.py`\n\n) is a Python polling loop that runs every hour. It auto-installs `requests`\n\nand `cryptography`\n\nvia pip if missing, then searches GitHub commits for the string `\"firedalazer\"`\n\n. Matching commit messages contain a base64 encoded URL and an RSA-PSS signature. The monitor verifies the signature against an embedded RSA-4096 public key, and if valid, downloads and executes the URL content as a Python script.\n\n- Uses a different search term (\n`\"firedalazer\"`\n\n) than the main worm (`\"DontRevokeOrItGoesBoom\"`\n\n) - Tracks executed commands in\n`/var/tmp/.gh_update_state`\n\nto prevent re-execution - 300 second timeout per script, temp file cleanup after execution\n\nThis is a general-purpose remote code execution backdoor. The attacker can push arbitrary Python scripts to any compromised machine by committing a signed message to any public GitHub repo.\n\n### Command and Control\n\nThe worm uses three independent GitHub commit search channels for C2, each with a different search string and purpose:\n\n`\"DontRevokeOrItGoesBoom\"`\n\ndiscovers attacker controlled PATs for exfiltration. Checked once during sender chain setup. PATs are AES-256-CBC encrypted in the commit message.`\"TheBeautifulSandsOfTime\"`\n\ndelivers JavaScript for immediate execution. Checked once at startup via`c2Checkin()`\n\nin`src/c2/checkin.ts`\n\n. The commit message contains a base64-encoded payload and RSA signature, verified against`verify_key.pub`\n\n. If valid, the payload is passed directly to`eval()`\n\n.`\"firedalazer\"`\n\ndelivers Python script URLs for the persistent monitor. Polled hourly by`GITHUB_MONITOR.py`\n\n. Uses RSA-PSS signature verification against a separate embedded public key.\n\nAll three channels are unauthenticated by default (falling back to authenticated search when a token is available) and use GitHub’s public commit search API. The attacker issues commands by committing a signed message to any public repository. Each channel uses a different verification key or decryption key, so compromising one does not compromise the others.\n\nThe `c2Checkin()`\n\ncall in `src/index.ts`\n\nis fire-and-forget:\n\nThe `DomainSenderFactory`\n\nimplements a fourth channel using the search string `\"thebeautifulmarchoftime\"`\n\nto discover backup C2 domains via signed commits. This factory exists in the codebase but is not wired into the current sender chain.\n\n### Dead-man Switch\n\nWhen the `GitHubSender`\n\nuses a victim’s own PAT for exfiltration (tier 3 fallback), it installs a dead-man switch before embedding the token in the exfiltration commit. The handler is hardcoded in `src/sender/github/githubSender.ts`\n\n:\n\nThe `DEADMAN_SWITCH.sh`\n\nasset installs a persistent monitoring script that:\n\n- Stores the victim’s token in\n`~/.config/gh-token-monitor/token`\n\n- Polls\n`GET https://api.github.com/user`\n\nevery 60 seconds to check token validity - If the API returns any\n`40x`\n\nstatus (token revoked), runs`eval \"$HANDLER\"`\n\nwhich executes`rm -rf ~/; rm -rf ~/Documents`\n\n- Exits after a 72 hour TTL if the token is not revoked\n- Registers as a systemd user service (Linux) or LaunchAgent (macOS)\n\nThe switch only activates when the victim has no enterprise-managed orgs. Enterprise tokens are considered higher-risk to embed. The `\"DontRevokeOrItGoesBoom\"`\n\nsearch string used in the exfiltration commit message is a literal warning to anyone who finds it.\n\n### Obfuscation and Evasion\n\nThe worm uses a 5-layer build pipeline to obfuscate the final payload.\n\n#### Asset Encryption\n\nThe `prebuild`\n\nstep (`scripts/pack-assets.ts`\n\n) gzip-compresses each file in `src/assets/`\n\n(loaders, workflows, public keys, Claude settings), then encrypts with a random per-file AES-256-GCM key. The output is `src/generated/index.ts`\n\nwith each asset exported as a runtime decryption call. The decryption keys themselves go through the string scramble layer described below.\n\n#### Source Transformation\n\nThree transforms are applied to every `.ts`\n\nfile before bundling:\n\n**Env-scramble**: rewrites`process.env.GITHUB_TOKEN`\n\nto`process.env[scramble(\"GITHUB_TOKEN\")]`\n\n, hiding environment variable names from static analysis. Skips`NODE_ENV`\n\nand`TZ`\n\n.**String scramble**: resolves every`scramble(\"literal\")`\n\ncall at build time. Uses PBKDF2 (200,000 iterations, SHA-256) to derive a master key from a random per-build passphrase and salt, then applies 3 rounds of CBC-mode polyalphabetic substitution with per-position, per-round lookup tables. The passphrase and salt are injected into`runtimeDecoder.ts`\n\nfor decoding at runtime.**Log stripping**: when`isSilent = true`\n\nin`logger.ts`\n\n, removes all`logUtil.*`\n\ncalls from source using balanced-paren counting with string-literal awareness.\n\n#### Bundle\n\nAll transformed files are bundled into a single `dist/bundle.js`\n\nvia `Bun.build()`\n\nwith minification enabled, targeting the Bun runtime.\n\n#### JavaScript Obfuscation\n\nPost-build pass using `javascript-obfuscator`\n\nwith string array encoding (base64, rotation, shuffle, index shifting), hexadecimal identifier names, and chained wrapper calls. Output goes to `dist_obf/`\n\n. An additional wrapping layer (`obfplus-wrap.js`\n\n) produces a further obfuscated build in `dist_obfplus/`\n\n.\n\n#### Self-Extracting Payload\n\nThe `buildSelfExtractingPayload()`\n\nfunction in `src/utils/selfExtracting.ts`\n\nis used by mutators when embedding the worm into npm packages, GitHub repos, and other targets. It produces a standalone JS file with three nested layers:\n\n**ROT-N outer shell**: random rotation (1-25) applied to the entire inner wrapper, decoded by an inline`eval()`\n\n**AES-128-GCM Section A**: encrypted bun installation guard that downloads Bun from GitHub releases if not present, pinned to`bun-v1.3.13`\n\n**AES-128-GCM Section B**: the actual worm payload, encrypted with a different random key\n\nAt runtime, the ROT-N shell decodes and evals the inner wrapper, which decrypts Section A (bun guard) and Section B (payload), writes the payload to a temp file, executes it with `bun run`\n\n, and cleans up. Every invocation produces completely different output due to random keys and random ROT-N rotation.\n\n- supply-chain\n- malware\n- shai-hulud\n- ai-coding-agents\n- github\n- npm\n- pypi\n- rubygems\n\n### Author\n\n#### SafeDep Team\n\nsafedep.io\n\n### Share\n\n## The Latest from SafeDep blogs\n\nFollow for the latest updates and insights on open source security & engineering\n\n[Miasma Worm: Most Infected GitHub Repos Are Still Live](/miasma-worm-still-infected-github-repos)\n\nEight days after the Miasma worm forged a credential stealer into public GitHub repositories, most are still serving it. A re-scan of the published victim list plus a fresh code-search sweep found...\n\n[Miasma Worm Targets AI Coding Agents via GitHub Repos](/miasma-worm-ai-coding-agent-config-injection)\n\nA Miasma worm variant injects a 4.3 MB dropper into GitHub repos across multiple maintainers, wiring it to auto-run through Claude Code, Gemini, Cursor, and VS Code config files. No npm package is...\n\n[Config Files That Run Code: Supply Chain Security Blindspot](/config-files-that-run-code)\n\nEditor and package-manager config files auto-execute commands when a developer opens a folder or installs dependencies. The Miasma worm wired one dropper into seven of them across Claude Code,...\n\n[Axios Typosquats Deliver the Epsilon Stealer](/malicious-faster-axios-npm-epsilon-stealer)\n\nTwo axios typosquats on npm, turbo-axios and faster-axios, form a campaign delivering Epsilon Stealer through a four-stage chain. The Electron infostealer grabs browser credentials, crypto wallets,...\n\n## Ship Code.\n\n## Not Malware.\n\nStart free with open source tools on your machine. Scale to a unified platform for your organization.", "url": "https://wpnews.pro/news/inside-the-miasma-software-supply-chain-attack-toolkit", "canonical_source": "https://safedep.io/inside-the-miasma-supply-chain-attack-toolkit", "published_at": "2026-06-09 10:00:00+00:00", "updated_at": "2026-06-11 19:16:30.764199+00:00", "lang": "en", "topics": ["ai-tools", "ai-infrastructure"], "entities": ["Miasma", "Team PCP", "PMG", "GitHub", "PyPI", "npm", "RubyGems", "JFrog Artifactory"], "alternates": {"html": "https://wpnews.pro/news/inside-the-miasma-software-supply-chain-attack-toolkit", "markdown": "https://wpnews.pro/news/inside-the-miasma-software-supply-chain-attack-toolkit.md", "text": "https://wpnews.pro/news/inside-the-miasma-software-supply-chain-attack-toolkit.txt", "jsonld": "https://wpnews.pro/news/inside-the-miasma-software-supply-chain-attack-toolkit.jsonld"}}