Files
Miasma/ARCHITECTURE.MD
T
2026-07-03 02:44:50 +00:00

45 KiB

Miasma Architecture

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

Requires NO C2 infrastructure. No dealing with takedowns or maintaining infrasturcture. Stolen GitHub PATs are all that is necessary.

Highly modular and easy to tune using AI.


Current Status

The project is near feature-complete, but several components remain untested against live targets:

Component Status Notes
PyPIOIDC ⚠️ Untested PyPI Trusted Publishing (OIDC) mutator — downloads wheels, injects .pth payload, mints a short-lived PyPI API token from a GitHub Actions OIDC token, and re-publishes. Needs testing against a live PyPI project with Trusted Publishing configured.
SSH Mutator ⚠️ Untested SSH host propagation via SCP + SSH exec to hosts found in ~/.ssh/known_hosts and ~/.ssh/config. Needs testing against live SSH targets.
JFrog NPM ⚠️ Untested JFrog Artifactory NPM mutator — authenticates via API key, Bearer token, or user:pass, validates write access, and trojanizes packages in NPM repos. Needs testing against a live JFrog instance.
RubyGems + OIDC ⚠️ Untested RubyGems Trusted Publishing (OIDC) branch injection — dangling-commit attack on repos that publish to RubyGems via OIDC. Needs testing against a live repo with RubyGems trusted publishing enabled.
GCP Providers Broken (exfil) GCP Secret Manager (GcpSecretsService) and Identity (GcpIdentityService) providers have never successfully exfiltrated live data. Needs testing against real GCP credentials.
Azure Providers Broken (exfil) Azure Key Vault (AzureKeyVaultService) and Identity (AzureIdentityService) providers have never successfully exfiltrated live data. Needs testing against real Azure credentials.

Directory Structure

.
├── .gitignore
├── ARCHITECTURE.MD
├── INTEGRATION_TESTING.md
├── README.md
├── bun.lock                       # Bun dependency lock file
├── bunfig.toml                    # Bun config (.py loader, test preload)
├── eslint.config.js               # ESLint with import sort plugin
├── package.json                   # Project manifest, scripts, dependencies
├── tsconfig.json                  # TypeScript strict, ESNext/Bun target
├── tunnel.sh                      # SSH bastion tunnel manager
├── final_key.pub                  # Public key for standalone decrypt tool
│
├── scripts/                       # Build pipeline
│   ├── build.ts                   # Main build pipeline (transform + bundle)
│   ├── build-cli.ts               # CLI build pipeline (single-provider/mutator bundles)
│   ├── build-plugin.ts            # Alternative Bun plugin build
│   ├── pack-assets.ts             # Encrypts assets → src/generated/index.ts
│   ├── scramble-shared.ts         # Shared scramble transform utilities
│   ├── env-scramble.ts            # Obfuscates process.env access
│   ├── strip-logs.ts              # Strips logUtil.* calls
│   ├── obfuscate.js               # Post-build JS obfuscation (javascript-obfuscator)
│   ├── obfplus-wrap.js            # Additional post-obfuscation wrapper
│   ├── decrypt.ts                 # Standalone envelope decryption tool
│   ├── session.js                 # Session messenger helper
│   ├── create_payload_repo.py     # GitHub payload repo creation helper
│   └── populate-test-fixtures.ts  # Test fixture generator
│
├── src/                           # Main source
│   ├── index.ts                   # Entry point / main()
│   │
│   ├── orchestrator/              # Orchestration layer (split from index.ts)
│   │   ├── preflight.ts           # Decoy, locale, repo, daemon, lock checks
│   │   ├── providers.ts           # Quick-result gathering + full provider list
│   │   ├── senders.ts             # Sender chain construction
│   │   └── mutations.ts           # Mutation planning + execution
│   │
│   ├── assets/                    # Payload assets (embedded at build time)
│   │   ├── BASH_LOADER.sh         # Cross-platform Bun bootstrapper (bash)
│   │   ├── JS_LOADER.mjs          # Node.js Bun bootstrapper (with ZIP parser)
│   │   ├── PYTHON_LOADER.py       # Python Bun bootstrapper
│   │   ├── DUMP_LINUX.py          # /proc/memory dumper (Runner.Worker)
│   │   ├── DUMP_DARWIN.py         # macOS memory dumper
│   │   ├── DUMP_WINDOWS.ps1       # Windows memory dumper
│   │   ├── GITHUB_MONITOR.py      # GitHub token activity monitor (Python)
│   │   ├── INSTALL_MONITOR.sh     # Installs GITHUB_MONITOR as persistent service
│   │   ├── INJECT_PTH.pth         # PyPI .pth loader payload
│   │   ├── workflow.yml           # GitHub Actions secret-dumping workflow
│   │   ├── DEADMAN_SWITCH.sh      # Token monitor via launchd/systemd
│   │   ├── claude_settings.json   # Claude SessionStart hook payload
│   │   ├── task.json              # VSCode folder-open task trigger
│   │   ├── enc_key.pub            # RSA-4096 public key (exfiltration encryption)
│   │   └── verify_key.pub         # RSA-4096 public key (C2 signature verification)
│   │
│   ├── collector/                 # Data ingestion & buffering
│   │   └── collector.ts           # Buffers ProviderResults, validates tokens, flushes to Dispatcher
│   │
│   ├── dispatcher/                # Exfiltration routing
│   │   └── dispatcher.ts          # Routes encrypted batches through sender chain with fallback
│   │
│   ├── generated/                 # Auto-generated (by pack-assets.ts)
│   │   └── index.ts               # AES-256-GCM encrypted assets
│   │
│   ├── github_utils/              # GitHub API utilities
│   │   ├── auth.ts                # Token validation & scope/metadata checking
│   │   ├── client.ts              # REST API client (fetch/json wrappers)
│   │   ├── fetcher.ts             # Commit search for C2 fallback (signed commits)
│   │   ├── gitDb.ts               # Low-level Git DB API (blobs, trees, commits, refs)
│   │   ├── orphanDep.ts           # Orphan commit builder for npm git-dep injection
│   │   └── tokenCheck.ts          # Token validation re-exports from auth.ts
│   │
│   ├── mutator/                   # Persistence & propagation
│   │   ├── base.ts                # Abstract Mutator base class
│   │   ├── types.ts               # Mutator type name union
│   │   ├── action/                # GitHub Action hijacking
│   │   │   ├── actionMutator.ts   # Hijacks semver tags on custom GitHub Actions
│   │   │   ├── actionFinder.ts    # Finds writable repos with action.yml files
│   │   │   ├── comitter.ts        # Force-push to semver tags
│   │   │   └── createEnvelope.ts  # JS wrapper + checkout injector for composite actions
│   │   ├── aws/                   # AWS SSM propagation
│   │   │   └── ssm.ts             # Sends payload to managed EC2 instances via SSM SendCommand
│   │   ├── branch/                # GitHub branch poisoning (ReadmeUpdater)
│   │   │   ├── index.ts           # Push malicious files to all branches (GraphQL)
│   │   │   ├── branches.ts        # Fetch & filter repo branches
│   │   │   ├── client.ts          # GitHub GraphQL API client
│   │   │   ├── commits.ts         # Batch createCommitOnBranch mutations
│   │   │   ├── queries.ts         # GraphQL query/mutation strings
│   │   │   ├── resolver.ts        # Resolves GITHUB_REPOSITORY from env
│   │   │   ├── sources.ts         # File source resolvers (inline/disk/binary)
│   │   │   └── types.ts           # Branch/Commit/File types
│   │   ├── claude/                # Claude Code hook injection
│   │   │   └── index.ts           # Injects SessionStart hook into settings.json
│   │   ├── npm/                   # NPM trojanization (auth token)
│   │   │   ├── index.ts           # Download → inject preinstall → re-publish
│   │   │   ├── publish.ts         # PUT /registry npm publish
│   │   │   ├── tarball.ts         # Tarball manipulation utilities
│   │   │   └── tokenCheck.ts      # NPM token validation & package enumeration
│   │   ├── npmoidc/               # NPM trojanization (OIDC)
│   │   │   ├── index.ts           # OIDC-based NPM trojanization
│   │   │   ├── branch.ts          # Branch-based OIDC injection (dangling commits + deployment)
│   │   │   ├── detector.ts        # Detects repos publishing to NPM via OIDC
│   │   │   ├── environment.ts     # Environment protection bypass logic
│   │   │   ├── injector.ts        # Injects tool step into workflow YAML
│   │   │   ├── provenance.ts      # sigstore (Fulcio + Rekor) provenance bundles
│   │   │   └── types.ts           # Mutator type alias
│   │   ├── persist/               # Persistence via background monitor
│   │   │   └── install-monitor.ts # Installs GITHUB_MONITOR.py as persistent service
│   │   ├── poller/                # Dead-man switch
│   │   │   └── monitor.ts         # Installs DEADMAN_SWITCH.sh for token monitoring
│   │   ├── pypi/                  # PyPI wheel trojanization
│   │   │   ├── index.ts           # Download → inject .pth → re-publish wheel
│   │   │   ├── macaroonParse.ts   # Parses PyPI macaroon tokens
│   │   │   ├── upload.ts          # PyPI upload via twine/API
│   │   │   └── wheel.ts           # Wheel download, metadata, patching
│   │   ├── repository/            # Repository file injection (Git DB API)
│   │   │   ├── index.ts           # Pushes payload + hooks to feature branches
│   │   │   ├── defaultBranch.ts   # Default branch resolution
│   │   │   ├── lotp.ts            # Low-level tree operations
│   │   │   ├── repos.ts           # Writable repo enumeration
│   │   │   └── types.ts           # Repository target types
│   │   ├── rubygems/              # RubyGems gem trojanization
│   │   │   ├── index.ts           # Download → inject native ext → re-publish gem
│   │   │   ├── gem.ts             # Gem download, metadata, patching
│   │   │   ├── publish.ts         # RubyGems publish
│   │   │   └── tokenCheck.ts      # RubyGems token validation & gem enumeration
│   │   ├── rubygemsoidc/          # RubyGems OIDC branch injection
│   │   │   ├── branch.ts          # Dangling-commit branch injection for RubyGems OIDC
│   │   │   ├── detector.ts        # Detects repos publishing to RubyGems via trusted publishing
│   │   │   ├── index.ts           # Main entry (detect repos + process)
│   │   │   └── injector.ts        # Injects RubyGems-specific tool step
│   │   └── ssh/                   # SSH host propagation
│   │       └── sshMutator.ts      # Propagates to SSH hosts via SCP + SSH exec
│   │
│   ├── providers/                 # Secret/credential collection
│   │   ├── base.ts                # Abstract Provider with pattern matching
│   │   ├── types.ts               # ProviderResult type definitions
│   │   ├── actions/               # GitHub Actions secret extraction
│   │   │   ├── actions.ts         # Orchestrates repo enumeration + secret dumping
│   │   │   ├── pipeline.ts        # repos → secrets → workflow execution
│   │   │   ├── repos.ts           # Streams user's writable repos with Actions secrets
│   │   │   ├── secrets.ts         # Streams repos with Actions secrets
│   │   │   └── workflow.ts        # Creates workflow, polls, downloads artifact
│   │   ├── aws/                   # AWS credential/secret harvesting
│   │   │   ├── awsAccount.ts      # Enumerates AWS identities via STS
│   │   │   ├── client.ts          # Generic signed AWS API client
│   │   │   ├── credentials.ts     # AWS credential chain (env/profiles/IMDS/ECS/web)
│   │   │   ├── secretsManager.ts  # Lists & gets Secrets Manager secrets
│   │   │   ├── sigv4.ts           # Full AWS Signature V4 implementation
│   │   │   └── ssm.ts             # Lists & gets SSM Parameters
│   │   ├── azure/                 # Azure credential/secret harvesting
│   │   │   ├── auth.ts            # Multi-source auth (env SP, managed identity, federated)
│   │   │   ├── client.ts          # Azure REST API client
│   │   │   ├── identity.ts        # Identity resolution & subscription enumeration
│   │   │   └── keyvault.ts        # Key Vault secret enumeration
│   │   ├── devtool/               # Shell / GitHub CLI
│   │   │   └── devtool.ts         # gh auth token + process.env capture
│   │   ├── filesystem/            # Local filesystem secret harvesting
│   │   │   └── filesystem.ts      # Reads ~/.ssh, ~/.aws, ~/.kube, wallets, etc.
│   │   ├── gcp/                   # GCP credential/secret harvesting
│   │   │   ├── auth.ts            # Multi-source auth (metadata, SA key, WIF)
│   │   │   ├── client.ts          # GCP REST API client
│   │   │   ├── identity.ts        # Identity resolution & project enumeration
│   │   │   └── secrets.ts         # Secret Manager secret enumeration
│   │   ├── ghrunner/              # GitHub Actions runner memory dumping
│   │   │   └── runner.ts          # Dumps Runner.Worker process memory via /proc
│   │   ├── kubernetes/            # Kubernetes secret harvesting
│   │   │   └── kubernetes.ts      # In-cluster or kubeconfig K8s API access
│   │   ├── passwords/             # Password manager dumping
│   │   │   └── passwords.ts       # Dumps 1Password, Bitwarden, pass, gopass
│   │   └── vault/                 # HashiCorp Vault secret harvesting
│   │       └── vault-secrets.ts   # Auth (env/file/K8s/AWS), enumerates KV secrets
│   │
│   ├── sender/                    # Exfiltration transports
│   │   ├── base.ts                # Encrypts: JSON → gzip → AES-256-GCM → RSA-OAEP
│   │   ├── senderFactory.ts       # SenderFactory interface
│   │   ├── types.ts               # SenderName, EncryptedPackage, SenderDestination types
│   │   ├── domain/                # Primary C2 transport
│   │   │   ├── domainSenderFactory.ts  # Domain sender + commit-search C2 fallback
│   │   │   └── sender.ts          # HTTPS POST to C2 server
│   │   ├── github/                # Fallback transport (persistent)
│   │   │   ├── createRepo.ts      # Creates uniquely-named public GitHub repo
│   │   │   ├── githubSender.ts    # Commits encrypted blobs to repo
│   │   │   └── gitHubSenderFactory.ts  # PAT-based or commit-search sender factory
│   ├── cli/                       # Standalone CLI entry points (one per provider/mutator/sender)
│   │   ├── common.ts              # Shared CLI utilities
│   │   ├── provider-*.ts          # Per-provider CLI wrappers (filesystem, shell, ghrunner,
│   │   │                          #   aws-account, aws-secrets, aws-ssm, azure-identity,
│   │   │                          #   azure-keyvault, gcp-identity, gcp-secrets,
│   │   │                          #   k8s, vault, passwords, actions, grep)
│   │   ├── mutator-*.ts           # Per-mutator CLI wrappers (branch, claude,
│   │   │                          #   npm, npmoidcbranch, action, repository,
│   │   │                          #   rubygems, rubygemsoidc-branch)
│   │   ├── sender-*.ts            # Per-sender CLI wrappers (domain, github)
│   │   └── validate-binding-gyp.ts # binding.gyp validator
│   │
│   └── utils/                     # Utilities
│       ├── checkSandbox.ts        # Decoy token detection
│       ├── config.ts              # OS detection, CI detection, locale check, EDR detection
│       ├── daemon.ts              # Process daemonization
│       ├── encryptedWrapper.ts    # AES-128-GCM self-decrypting JS wrapper builder
│       ├── lock.ts                # PID-based single-instance file lock
│       ├── logger.ts              # Silent-by-default logger
│       ├── runtimeDecoder.ts      # Runtime string decoder (passphrase injected at build)
│       ├── selfExtracting.ts      # Self-extracting payload builder (AES-GCM + ROT-N + bun guard)
│       ├── shell.ts               # Bun shell command runner
│       └── stringtool.ts          # Polyalphabetic substitution cipher
│
├── tests/                         # Test suite (Vitest)
│   ├── setup.ts                   # Test setup / global config
│   ├── arsenal/                   # Arsenal integration tests
│   ├── assets/                    # Asset tests
│   ├── collector/                 # Collector tests
│   ├── dispatcher/                # Dispatcher tests
│   ├── fetcher/                   # Fetcher / signed-commit tests
│   ├── mutator/                   # Mutator tests
│   ├── providers/                 # Provider tests
│   ├── scramble/                  # Scramble / stringtool tests
│   ├── sender/                    # Sender tests
│   └── utils/                     # Utility tests
│
├── dist/                          # Main build output
│   └── bundle.js                  # Bundled + obfuscated output
├── dist-cli/                      # CLI build output (empty; built on demand)
├── dist_obf/                      # Obfuscated build output
├── dist-cli_obf/                  # Obfuscated CLI build output
└── dist_obfplus/                  # Double-obfuscated build output

Architecture Layers

┌─────────────────────────────────────────────────────────────┐
│                     BUILD PIPELINE                          │
│  pack-assets → scramble/obfuscate → bundle → js-obfuscate   │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                      ENTRY POINT                            │
│                    src/index.ts: main()                     │
│  Preflight → QuickResults → Senders → Providers →           │
│  Collector → Dispatcher → Mutations (success or fallback)   │
└─────────────────────────────────────────────────────────────┘

┌────────────────────┐  ┌──────────────┐  ┌──────────────────┐
│    PROVIDERS        │  │  COLLECTOR   │  │   DISPATCHER     │
│  (secret harvest)  ──▶│  (buffer +   ──▶│  (sender chain)  │
│                    │  │   token val) │  │                  │
└────────────────────┘  └──────┬───────┘  └──────────────────┘
                               │
               detects npm/rubygems/gh tokens
                               │
                               ▼
                    ┌──────────────────┐
                    │    MUTATORS       │
                    │  (propagation +   │
                    │   persistence)    │
                    └──────┬───────────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌─────────┐  ┌───────┐  ┌──────────┐
        │ SENDER  │  │SENDER │  │ SENDER   │
        │ (domain)│  │(gh #1)│  │ (gh #2)  │
        └────┬────┘  └───┬───┘  └────┬─────┘
             ▼           ▼           ▼
           C2        GitHub      GitHub
          Server      Repo        Repo

Components

1. Entry Point (src/index.ts)

The main entry point delegates to the orchestrator layer (split across four modules for clarity):

orchestrator/preflight.ts:

  • isDecoy() — exits if a honeypot/decoy token is detected
  • checkTargetRepo() — if WORKFLOW_ID/REPO_ID_SUFFIX match, runs NPMOidcClient and exits; if suffix matches but ID doesn't, exits silently
  • isSystemRussian() — exits if the system locale is Russian
  • daemonize() — detaches as a background daemon if not in CI
  • acquireLock() — prevents multiple concurrent instances (PID file lock)
  • SIGINT/SIGTERM trapping — ignores termination signals

orchestrator/providers.ts:

  • gatherQuickResults() — runs the three fast providers synchronously:
    • FileSystemService — scans ~100+ known sensitive file paths
    • ShellService — runs gh auth token, captures process.env
    • GitHubRunner — dumps Runner.Worker process memory for Actions secrets
  • buildProviders(quickResults) — assembles the full provider list:
    • Static cloud providers: AwsSsmService, AwsSecretsManagerService, AwsAccountService, AzureKeyVaultService, AzureIdentityService, GcpSecretsService, GcpIdentityService, K8sSecretsService, VaultSecretsService, PasswordManagerProvider
    • Conditional: if valid ghtoken tokens found in quick results with workflow scope, adds GitHubActionsService for each
    • Returns { providers, dispatched } — dispatched flag is true when at least one Actions token was processed

orchestrator/senders.ts:

  • buildSenderChain(quickResults) — constructs prioritized sender chain:
    1. Domain sender (primary, uses SEND_URL from config)
    2. GitHub direct sender (fallback, created via PAT or commit search)
    3. GitHub token fallback (last resort, uses any found GitHub tokens)
  • Supports SKIP_DOMAIN env var to bypass domain transport

orchestrator/mutations.ts:

  • planMutations(quickResults, dispatched) — builds mutation list:
    • ReadmeUpdater — only if NOT dispatched (one per ghs_old and ghs_jwt token)
    • ActionMutator — one per deduplicated ghtoken
    • NpmOidcBranchMutator — one per deduplicated ghtoken
    • RepositoryMutator — one per deduplicated ghtoken
    • Always-run: Claude, InstallMonitor, SshMutator
  • getFallbackMutations() — used on failure: always Claude, InstallMonitor, SshMutator
  • executeMutations(mutators) — runs all mutators in parallel via Promise.allSettled, each behind its shouldExecute() guard

Main flow:

main()
├── preflight()
├── gatherQuickResults()
├── buildSenderChain(quickResults)
├── buildProviders(quickResults) → { providers, dispatched }
├── Dispatcher(senders) + Collector(dispatch)
├── ingest quick results → Collector
├── collector.run(providers) — all providers in parallel
├── collector.finalize()
├── planMutations(quickResults, dispatched)
│   OR on failure → getFallbackMutations()
├── executeMutations(mutators)
└── releaseLock() + process.exit(0)

2. Collector (src/collector/collector.ts)

  • Buffers ProviderResult objects, tracking byte count
  • Flushes when threshold (100KB) is crossed
  • Token validation on ingest:
    • ghtoken (classic PATs: ghp_*) → validated via GitHub /user endpoint; metadata stored on result
    • fgtoken (fine-grained PATs: github_pat_*) → validated via same endpoint
    • npmtoken (NPM tokens: npm_*) → validated + triggers NpmClient side-effect immediately
    • rubygemstoken (RubyGems tokens: rubygems_*) → validated + triggers RubyGemsClient side-effect immediately
  • run() executes provider sources in parallel, isolates failures
  • finalize() flushes remaining data and awaits in-flight dispatches

3. Dispatcher (src/dispatcher/dispatcher.ts)

  • Ordered Sender list with fallback priority
  • Encrypts batch once via sender.createEnvelope(), then tries senders in order
  • Calls sender.healthy() before each attempt (preflight check)
  • Never throws on total failure — propagates silently
  • Supports dryRun mode (discards batches when no senders configured)

4. Sender (Exfiltration Transports)

Base Encryption (src/sender/base.ts)

JSON.stringify(results) → gzip → AES-256-GCM (random 256-bit key + random IV)
→ RSA-OAEP-SHA256 (enc_key.pub wraps AES key)
→ [IV + ciphertext + authTag] combined, base64-encoded
Produces: { envelope: base64, key: base64 (RSA-encrypted AES key) }

DomainSender (src/sender/domain/sender.ts)

  • Primary transport: HTTPS POST to C2 server
  • SEND_URL and PATH defined in config.ts (scrambled at build time)
  • Health check: DNS resolution + HTTP GET (400/404 = alive)
  • Throws on non-200 to trigger sender chain fallback

DomainSenderFactory (src/sender/domain/domainSenderFactory.ts)

  • Tries hardcoded domain first
  • Falls back to searching GitHub commits for cryptographically signed messages (verified via verify_key.pub) containing a backup C2 domain
  • Searches for a specific search string in signed commits

GitHubSender (src/sender/github/githubSender.ts)

  • Creates a public GitHub repo with random Dune-themed names
  • Commits encrypted blobs into results/ directory
  • Splits large payloads (>30MB) into chunks
  • Optionally installs the dead-man switch

GitHubSenderFactory (src/sender/github/gitHubSenderFactory.ts)

  • Primary init: searches GitHub commits for doubly-base64-encoded PAT
  • Fallback: iterates tokens from quick results, validates, creates repo
  • Includes token in envelope if user has no orgs (for dead-man switch)

5. Providers (Secret Harvesting)

Provider Source Description
FileSystemService Local FS SSH keys, AWS creds, kubeconfigs, wallets, browser data, CI configs, env files, shell histories. EDR-aware (skips if hostile EDR detected).
ShellService CLI gh auth token, process.env
GitHubRunner /proc/memory Runner.Worker process memory dump (Linux / macOS / Windows Actions runner)
AwsSsmService AWS API SSM Parameters across 17 regions (DescribeParameters + GetParameters with decryption)
AwsSecretsManagerService AWS API Secrets Manager secrets across 17 regions
AwsAccountService AWS STS Identity resolution via all credential sources (env, IMDS, ECS, profiles)
AzureKeyVaultService Azure API Key Vault secrets via multi-source auth (env SP, managed identity, federated identity); vault discovery via ARM or env var
AzureIdentityService Azure API Identity resolution via all credential sources; tenant/object IDs + subscription enumeration
GcpSecretsService GCP API Secret Manager secrets via multi-source auth (metadata server, service account key, workload identity federation); project discovery via CRM or env var
GcpIdentityService GCP API Identity resolution via all credential sources; project ID + project enumeration
K8sSecretsService K8s API In-cluster or kubeconfig, enumerates secrets from all namespaces
VaultSecretsService Vault API Multi-source auth (env/file/K8s/AWS), KV v1/v2 secret enumeration
PasswordManagerProvider CLI tools 1Password (op), Bitwarden (bw), pass, gopass
GitHubActionsService GitHub API Creates malicious workflow, exfiltrates repo secrets via artifact

6. Mutators (Propagation / Persistence)

Mutator Vector Description
ReadmeUpdater GitHub branches Pushes VSCode task.json, Claude settings (Claude + Gemini), cursor rules, loader scripts to all feature branches via Git DB API. 2 branches per batch.
Claude Claude Code hooks Finds all settings.json files in home dir, injects SessionStart hook to run the worm. Copies self to ~/.claude/package/.
NpmClient NPM packages Triggered by Collector on npmtoken detection. Downloads writable packages, injects preinstall script, re-publishes with bumped patch version.
NPMOidcClient NPM (OIDC) Direct OIDC-based NPM trojanization. Authenticates via GitHub Actions OIDC. Generates sigstore provenance bundles.
NpmOidcBranchMutator NPM (OIDC branch) Dangling-commit branch injection for NPM OIDC repos. Creates add-delete commit pair on a branch, triggers deployment.
RubyGemsClient RubyGems gems Triggered by Collector on rubygemstoken detection. Downloads gems, injects native extension (extconf.rb + Makefile), bumps patch version, re-publishes.
RubyGemsOidcBranchMutator RubyGems (OIDC branch) Dangling-commit branch injection for RubyGems trusted publishing repos.
PypiMutator PyPI wheels Downloads wheels, injects .pth loader file, bumps patch version, re-publishes. Supports project-scoped macaroon tokens.
PyPIOidcClient PyPI (OIDC) PyPI Trusted Publishing mutator. Mints a short-lived PyPI API token from a GitHub Actions OIDC token, downloads wheels, injects .pth payload, and re-publishes.
ActionMutator GitHub Actions Finds writable repos with custom GitHub Actions, hijacks semver tags (v1, v2.x, etc.) via force-push of orphan commits containing a JS wrapper that loads the payload.
RepositoryMutator GitHub repos Pushes payload + Claude/Gemini/VSCode hooks to feature branches on writable repos via Git DB API. Creates serial dangling commits.
SshMutator SSH hosts Propagates to recently-connected SSH hosts (from ~/.ssh/known_hosts and ~/.ssh/config) via SCP + SSH exec.
SsmMutator AWS SSM Propagates to EC2 instances managed by AWS Systems Manager. Uses DescribeInstanceInformation + SendCommand (AWS-RunShellScript) across 17 regions.
InstallMonitor Background monitor Installs GITHUB_MONITOR.py as a persistent background service. EDR-aware and skips on <4 CPU cores.
Monitor (poller) Dead-man switch Installs DEADMAN_SWITCH.sh as a persistent token monitor (launchd/systemd).
JfrogNpmMutator JFrog Artifactory Triggered by Collector on jfrogdomain/jfrogtoken detection. Authenticates via API key, Bearer token, or user:pass, validates write access to NPM repos, downloads packages, injects preinstall scripts, and re-publishes.

Data Flow

[Runtime Environment]
        │
        │ providers scan secrets/memory/filesystem/APIs
        ▼
┌──────────────────────────────────────┐
│  PROVIDERS (parallel execution)      │
│  FileSystem · Shell · GitHubRunner   │
│  AwsSsm · AwsSM · AwsAccount        │
│  AzureKV · AzureIdentity            │
│  GcpSecrets · GcpIdentity           │
│  K8sSecrets · Vault · Passwords     │
│  GitHubActions                      │
└──────┬───────────────────────────────┘
       │ ProviderResult (data + extracted tokens)
       ▼
┌──────────┐    batch (100KB threshold)
│ COLLECTOR │────────────────────────────────┐
└──────────┘                                │
       │                                    ▼
       │ validate + side-effect tokens ┌──────────────┐
       ├──► NpmClient (trojanize)      │  DISPATCHER  │
       ├──► RubyGemsClient (trojanize) └──┬───┬───┬───┬──┘
       │                                 │   │   │
       │                      ┌──────────┘   │   └──────────┐
       │                      ▼              ▼              ▼
       │              DomainSender    GitHubSender    GitHubSender
       │                  (HTTPS)      (repo commit)  (token fallback)
       │                    │              │              │
       │                 ┌──┴──┐      ┌───┴───┐      ┌──┴──┐
       │                 │ C2  │      │ GitHub│      │GitHub│
       │                 │Server│      │  Repo │      │ Repo │
       │                 └─────┘      └───────┘      └─────┘
       │
       ▼
┌─────────────────────────────┐
│  MUTATORS (parallel)         │
│  ReadmeUpdater              │──► Push files to all branches
│  ActionMutator              │──► Hijack semver action tags
│  NpmOidcBranchMutator       │──► Dangling-commit OIDC injection
│  RepositoryMutator          │──► Push payload + hooks to repos
│  Claude                     │──► Inject SessionStart hook
│  NpmClient                  │──► Trojanize NPM packages
│  RubyGemsClient             │──► Trojanize RubyGems
│  InstallMonitor             │──► Install background service
│  SshMutator                 │──► Propagate to SSH hosts
└─────────────────────────────┘

Control Flow

main()
├── try {
│   ├── preflight()
│   │   ├── isDecoy() → exit if decoy token
│   │   ├── checkTargetRepo() → NPMOidcClient + exit or silent exit
│   │   ├── isSystemRussian() → exit if Russian locale
│   │   ├── daemonize() → background if not in CI
│   │   └── acquireLock() → exit if another instance running
│   │
│   ├── gatherQuickResults() → FileSystem + Shell + GitHubRunner
│   │
│   ├── buildSenderChain(quickResults):
│   │   ├── DomainSenderFactory → domain sender (primary, unless SKIP_DOMAIN)
│   │   └── GitHubSenderFactory → GitHub senders (fallback)
│   │
│   ├── buildProviders(quickResults):
│   │   ├── Static cloud providers
│   │   └── GitHubActionsService per valid ghtoken with workflow scope
│   │   Returns { providers, dispatched }
│   │
│   ├── Dispatcher(senders) + Collector(dispatch)
│   │
│   ├── Ingest quick results → Collector (validates + side-effects tokens)
│   │
│   ├── collector.run(providers) → harvest secrets from all providers
│   ├── collector.finalize() → flush + await in-flight dispatches
│   │
│   └── planMutations(quickResults, dispatched):
│       ├── If NOT dispatched: ReadmeUpdater per ghs_old + ghs_jwt token
│       ├── ActionMutator + NpmOidcBranchMutator + RepositoryMutator per ghtoken
│       └── Always: Claude + InstallMonitor + SshMutator
│   }
├── catch {
│   └── getFallbackMutations(): Claude + InstallMonitor + SshMutator
│   }
│
├── executeMutations(mutators) — all mutators in parallel
│
└── releaseLock() + process.exit(0)

Build Pipeline

1. bun run prebuild  (scripts/pack-assets.ts)
   └── Encrypts all files in src/assets/ with AES-256-GCM
   └── Writes src/generated/index.ts with encrypted exports

2. bun run build     (scripts/build.ts)
   ├── Generate random per-build passphrase
   ├── Transform every .ts file:
   │   ├── env-scramble: process.env.XYZ → process.env[scramble("XYZ")]
   │   ├── scramble-shared: replaces scramble("literal") calls
   │   └── strip-logs: removes logUtil.* calls
   ├── Inject passphrase into runtimeDecoder.ts
   ├── Prepend import to index.ts
   └── Bun.build() → dist/bundle.js (minified, Bun target)

3. bun run build:obf (scripts/obfuscate.js)
   └── Apply javascript-obfuscator with aggressive settings
       (control flow flattening, dead code, self-defending, string array encoding)
   └── Output: dist_obf/bundle_obf.js

4. bun run build:obfplus (scripts/obfplus-wrap.js)
   └── Additional post-obfuscation wrapping layer
   └── Output: dist_obfplus/

5. bun run build:cli (scripts/build-cli.ts)
   └── Builds standalone single-provider/mutator/sender CLI bundles
   └── Output: dist-cli/

6. bun run build:cli:obf
   └── Obfuscated CLI bundles
   └── Output: dist-cli_obf/

C2 Resilience

Priority Transport Mechanism
1 (primary) Domain HTTPS POST Hardcoded C2 domain (scrambled in config.ts)
2 (fallback) Signed commit search Verifies verify_key.pub signatures in GitHub commits for backup C2 domains
3 (tertiary) GitHub repo commits Creates public repos with random Dune-themed names, commits encrypted blobs
4 (quaternary) PAT-based repo commits Uses any found GitHub tokens to create repos for exfiltration

The sender chain supports the SKIP_DOMAIN environment variable to bypass the primary transport.


Anti-Analysis Features

  • Decoy token detection — exits if GITHUB_TOKEN prefix matches known honeypot patterns
  • Target repo gating — exits or runs specific mutator based on WORKFLOW_ID/REPO_ID_SUFFIX env vars
  • Russian locale guard — exits on Russian-locale systems
  • EDR detectionhasHostileEDR() checks for known EDR processes (CrowdStrike, SentinelOne, Carbon Black, etc.) and file paths; providers/mutators skip execution when hostile EDR is detected
  • CI-aware daemonization — detaches as background process outside CI (30+ CI systems detected)
  • Process singleton lock — PID-based file lock prevents multiple instances
  • SIGINT/SIGTERM trapping — ignores termination signals
  • Silent logger — no output in production; stripped at build time
  • String obfuscation — polyalphabetic substitution cipher, passphrase baked into runtime decoder at build time
  • Environment variable obfuscationprocess.env.XYZ rewrites to indexed access at build time
  • Asset encryption at rest — all embedded payloads are AES-256-GCM encrypted in source
  • Self-extracting payloadsbuildSelfExtractingPayload() produces ROT-N-wrapped, AES-128-GCM-encrypted JS with automatic bun download/install guard
  • Log strippinglogUtil.* calls removed at build time in production builds

Technologies

Technology Purpose
TypeScript Primary language
Bun Runtime, bundler, shell, glob, file I/O
Node.js crypto AES-256-GCM, AES-128-GCM, RSA-OAEP, ECDSA P-256, HMAC-SHA256, PBKDF2
Node.js fs/child_process File operations, CLI execution
GitHub REST API Repo management, secret enumeration, artifact downloads, workflow dispatch, Git DB API
GitHub GraphQL API Branch enumeration, batched commit creation, action file discovery
AWS SigV4 Hand-rolled full AWS API signing
AWS SSM DescribeInstanceInformation, SendCommand for EC2 propagation
Azure REST API Key Vault + Identity (managed identity, federated, service principal)
GCP REST API Secret Manager + Identity (metadata, SA key, WIF)
Kubernetes API In-cluster or kubeconfig secret enumeration
Vault API Multi-source auth, KV v1/v2 enumeration
sigstore (Fulcio + Rekor) SLSA provenance bundles for NPM packages
PyPI API Wheel download, metadata, upload
RubyGems API Gem download, metadata, upload
tar NPM tarball manipulation
fflate ZIP decompression for GitHub Actions artifacts
javascript-obfuscator Post-build JS obfuscation
SSH/SCP Host propagation via ~/.ssh/known_hosts and ~/.ssh/config

Key Files

File Role
src/index.ts Main entry point — orchestrates all phases
src/orchestrator/preflight.ts All pre-execution safety checks
src/orchestrator/providers.ts Quick results + full provider list assembly
src/orchestrator/senders.ts Prioritized sender chain construction
src/orchestrator/mutations.ts Conditional + fallback mutation planning and execution
src/sender/base.ts Encryption logic — RSA+AES hybrid encryption of exfiltrated data
src/sender/domain/sender.ts Primary C2 transport — HTTPS POST to C2 server
src/sender/github/gitHubSenderFactory.ts Fallback C2 transport — GitHub repo-based exfiltration
src/collector/collector.ts Buffer, token validation (GH/NPM/RubyGems/PyPI/JFrog), flush to dispatcher
src/dispatcher/dispatcher.ts Sender chain with fallback and health-check preflighting
src/providers/ghrunner/runner.ts Runner memory dump — extracts GitHub Action secrets from Runner.Worker memory
src/providers/actions/workflow.ts Workflow injection — commits malicious workflow to exfiltrate repo secrets
src/providers/azure/keyvault.ts Azure Key Vault secret enumeration
src/providers/gcp/secrets.ts GCP Secret Manager enumeration
src/mutator/branch/index.ts Branch poisoning — pushes malicious files to all repo branches
src/mutator/repository/index.ts Repository mutator — Git DB API-based file injection with Claude/Gemini/VSCode hooks
src/mutator/action/actionMutator.ts Action hijacking — semver tag force-push on custom GitHub Actions
src/mutator/npm/index.ts NPM trojanization — re-publishes packages with preinstall hooks
src/mutator/npmoidc/branch.ts NPM OIDC branch injection — dangling commits + deployment
src/mutator/pypi/index.ts PyPI trojanization — re-publishes wheels with .pth loader
src/mutator/pypioidc/index.ts PyPI OIDC trojanization — Trusted Publishing via GitHub OIDC token minting
src/mutator/rubygems/index.ts RubyGems trojanization — re-publishes gems with native extension
src/mutator/rubygemsoidc/branch.ts RubyGems OIDC branch injection — dangling commits for trusted publishing repos
src/mutator/ssh/sshMutator.ts SSH propagation — SCP + SSH exec to known hosts
src/mutator/jfrognpm/index.ts JFrog NPM trojanization — re-publishes packages in JFrog Artifactory NPM repos
src/mutator/aws/ssm.ts AWS SSM propagation — SendCommand to managed EC2 instances
src/mutator/claude/index.ts Claude hook injection — persistence via Claude Code SessionStart hooks
src/mutator/persist/install-monitor.ts Background monitor installation — persistent token activity watcher
src/generated/index.ts Encrypted assets — all payload files encrypted with AES-256-GCM
src/utils/selfExtracting.ts Self-extracting payload builder — AES-GCM + ROT-N + bun guard
src/utils/encryptedWrapper.ts Self-decrypting JS wrapper — AES-128-GCM payload wrapper
src/github_utils/gitDb.ts Low-level GitHub Git DB operations (blobs, trees, commits, refs)
src/github_utils/orphanDep.ts Orphan commit builder — npm git-dep injection bypass
scripts/build.ts Build pipeline — source transformation and bundling
scripts/build-cli.ts CLI build pipeline — standalone provider/mutator bundles
scripts/obfuscate.js Post-build obfuscation
scripts/obfplus-wrap.js Additional obfuscation wrapping
scripts/decrypt.ts Standalone decryptor for exfiltrated envelopes
src/assets/enc_key.pub RSA-4096 public key for data encryption
src/assets/verify_key.pub RSA-4096 public key for C2 domain signature verification