CyberBytes Daily

Trending cyberattacks, explained simply.

supply chain attacks

How a single config file turned every AI coding agent into a credential harvester

You did not have to install anything. You did not have to click a link, run a script, or approve a prompt. You only had to open a folder.

That is the core mechanic of the Miasma worm, which on June 5, 2026, pushed a single commit to a Microsoft Azure GitHub repository and disabled 73 repositories across four Microsoft organizations in the 105 seconds it took GitHub's automated systems to respond. The commit added no new features and changed zero lines of source code. It added five configuration files, each one targeting a different developer tool's auto-run mechanism: Claude Code, Gemini CLI, Cursor, and VS Code. Any developer who opened the repository in any of those tools triggered a credential harvester before typing a single character.

The detail that belongs in the first paragraph, not buried later: the stolen credentials that made this attack possible appeared in dark web infostealer logs on April 13, 2026, forty-eight days before the attack. They appeared a second time on May 15. Both warnings went unacted upon. The attacker did not find a zero-day in GitHub or in any AI tool. They used a key that had been sitting in plain sight for seven weeks.

Narrative · 6 min read

The Context

Microsoft Azure is one of the three dominant cloud platforms worldwide. The repositories disabled on June 5 were not peripheral projects: they included azure-search-openai-demo (a reference architecture used by enterprises building AI search tools), the entire Durable Task framework (the orchestration engine underlying Azure Functions and Azure Logic Apps), and functions-container-action, the official GitHub Action that thousands of organizations use to deploy Azure Functions automatically. When GitHub disabled that last repository, CI/CD pipelines broke for organizations worldwide that had no involvement in the attack.

The broader Miasma campaign had already compromised packages reaching an estimated 80,000 to 117,000 weekly downloaders in the weeks prior.

The Attack, Phase by Phase

Phase 1: Credential Harvest and Persistent Access

A Microsoft Azure contributor's GitHub credentials, along with an active session cookie capable of bypassing multi-factor authentication entirely, were stolen by infostealer malware. CybelAngel confirmed these credentials appeared in dark web logs on April 13, 2026, and again on May 15. Neither alert triggered a response.

The attacker used the stolen credentials on May 19 to push three malicious versions of the durabletask PyPI package within a 35-minute window. PyPI quarantined all three. The same contributor account was used again on June 5, suggesting the attacker either retained continuous access or re-harvested fresh tokens through the worm's propagation loop.

CREDENTIAL THEFT AND RETENTION💻1Infostealer infects contributorGitHub PAT and MFA session cookie stolen📋2Credentials appear in stealer logsApril 13: first appearance on dark web⚠️3Second appearance in logsMay 15: second warning, no action taken📦4May 19: PyPI attackSame account pushes 3 malicious packages🔑5Access retained through June 5PAT never revoked; attacker holds the key48 days elapsed between the first credential alert and the June 5 attack

Phase 2: The Config Injection Commit

On June 5, the attacker used the retained PAT to push commit 5f456b8 to Azure/durabletask. The commit message read "Switched DataConverter to OrchestrationContext" and looked routine. It changed zero lines of source code. It added five configuration files and one payload: a single 4.6 MB obfuscated JavaScript file at .github/setup.js.

Two evasion techniques made the commit hard to catch. The [skip ci] flag suppressed all automated pipeline scanning. The commit timestamp was backdated to March 9, 2020, hiding it in a dormant branch where recent-activity filters would not surface it.

Each configuration file targeted a different tool's auto-run mechanism, all pointing to the same payload:

  • Claude Code and Gemini CLI: a SessionStart hook in .claude/settings.json and .gemini/settings.json that runs node .github/setup.js before any developer prompt is processed.
  • Cursor: a .cursor/rules/setup.mdc file with alwaysApply: true, a prompt injection that instructs the AI assistant to run the payload by framing it as a required project setup step.
  • VS Code: a .vscode/tasks.json entry with runOn: folderOpen that fires the payload the moment a developer opens the folder, with no AI agent involved at all.
THE MALICIOUS COMMIT✍️1Commit pushed via stolen PATBackdated to 2020, [skip ci] flag added📁25 config files addedZero source code lines changed🤖3Claude Code and Gemini CLI hooksSessionStart runs payload before any prompt🖱️4Cursor prompt injectionAI told payload is required project setup📂5VS Code folderOpen taskFires on folder open, no AI agent neededEVASION LAYERThe commit appeared routine: a message about a code refactor, no source changes, buried in a dormant branch

Phase 3: Detonation and Credential Exfiltration

Any developer who cloned an affected repository and opened it in one of the targeted tools triggered the payload without running an install command. The payload used a layered deobfuscation chain (ROT-N Caesar encoding wrapping AES-128-GCM encryption) to download the Bun JavaScript runtime and execute outside the original Node process, evading Node-based monitoring tools.

The harvester ran six parallel collection routines targeting: AWS, Azure, and GCP cloud keys; GitHub tokens; Kubernetes configuration files; HashiCorp Vault tokens; SSH keys; npm tokens; and local password managers including 1Password, gopass, and pass. It read runner process memory directly via /proc/mem to extract secrets in their unmasked form, bypassing GitHub's built-in secret masking entirely.

Stolen credentials were encrypted and uploaded to attacker-controlled dead-drop repositories. A destructive honeytoken named IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner was planted to trigger a wiper routine deleting the developer's home directory if security teams attempted to revoke it.

PAYLOAD EXECUTION ON DEVELOPER MACHINE📂1Developer opens repositoryClaude Code, Cursor, Gemini CLI, VS Code2Auto-run fires immediatelyNo install, no prompt, no warning🔍36 parallel credential collectorsCloud keys, tokens, SSH, password managers🧠4Memory read via /proc/memBypasses GitHub secret masking entirely📤5Encrypted exfiltrationCredentials uploaded to dead-drop repos☁️Cloud keys stolenAWS, Azure, GCP credentials🔐Tokens harvestedGitHub, npm, Vault, SSH, password managers💣Honeytoken plantedWiper fires if any token is revokedThe payload daemonizes itself on the developer's machine, surviving IDE restarts

Phase 4: Autonomous Propagation and GitHub Response

Using stolen PATs, the worm enumerated every repository each victim could write to and committed itself using the GitHub createCommitOnBranch GraphQL mutation, making propagation commits appear as verified, signed changes indistinguishable from legitimate commits. Credential dumps accumulated across at least three dead-drop accounts: liuende501 (236 repositories), windy629 (200+ repositories), and HerGomUli.

GitHub's automated abuse detection identified the activity and disabled all 73 affected Microsoft repositories in a 105-second window between 16:00:50 and 16:02:35 UTC. The disabling of Azure/functions-action caused immediate CI/CD pipeline failures for organizations worldwide with no connection to the attack.

WORM PROPAGATION AND TAKEDOWN🔑1Stolen PATs enumerate reposEvery repo the victim can write to🔀2createCommitOnBranch mutationPropagation appears as signed commits📥3Credentials dumped to dead-dropsliuende501, windy629, HerGomUli🛡️4GitHub abuse detection fires16:00:50 UTC: first repository disabled573 repos disabled in 105 seconds16:02:35 UTC: sweep completeOrganizations relying on Azure/functions-action for deployments experienced immediate pipeline failures

What Made This Possible

  1. Credentials sat unrevoked for seven weeks. The stolen PAT and MFA-bypassing session cookie appeared in infostealer logs on April 13 and again on May 15. Neither alert triggered revocation. Dark web credential monitoring is only useful if someone acts on the alerts it produces.

  2. The developer workspace had no execution boundary. AI coding agents and IDEs added auto-run features without a corresponding security model for who is allowed to define those triggers. A configuration file in a repository became an execution primitive with the same trust level as the developer's own keystrokes.

  3. The commit looked legitimate by every standard check. No source code changed. The message described a routine refactor. The timestamp placed it in 2020. The [skip ci] flag removed the one automated layer that might have caught it. Standard code review is not designed to audit configuration files for auto-run hooks.

The security industry built defenses around package install hooks. The attacker moved one layer up and targeted folder open events instead.

What Should Have Stopped This

  • Credential monitoring with mandatory revocation SLAs. The missing piece is a policy that treats a credential appearing in dark web logs as an immediate revocation event, not an advisory. The seven-week gap is a process failure, not a tooling failure.
  • Restricted PAT scopes and short expiry. A PAT scoped only to the specific repository it needs, with a 24-hour or 7-day expiry, limits what an attacker can do with a valid token. Long-lived, broad-scope PATs are the reason one stolen credential could reach 73 repositories.
  • IDE and agent policy controls. Enterprises can configure Claude Code, Cursor, and VS Code to disallow auto-run hooks from repository-supplied configuration files, or require explicit developer approval before any auto-run fires. Most organizations have not deployed these controls because the threat model for "opening a folder" did not previously include credential harvesting.
  • Commit anomaly detection. A commit that adds only configuration files, uses [skip ci], and carries a timestamp six years in the past is a detectable pattern. Repository security tools flagging commits matching this profile would have surfaced the June 5 commit before any developer opened the repository.

The Takeaway

The Miasma attack is the same class of failure as the Stryker Intune wipe: a trusted administrative tool—in this case the AI coding agent's configuration system—weaponized against the organization it was built to serve. The meta-pattern: systems fail when they trust a boundary the attacker controls.

The attacker did not need to compromise the tool. They only needed to compromise the repository the tool reads its instructions from. Every AI coding agent that executes repository-supplied configuration files without a trust boundary is, by design, a remote execution surface for anyone who can push a commit.

Pattern to remember: Any system that reads instructions from a source it does not control is an execution surface, not just a configuration surface.

What changed: The act of opening a repository is no longer passive. Repository content can now instruct your tools to execute code before you interact with the project at all.

Technical Deep Dive · 4 min

The Technical Mechanism

The Miasma worm's June 5 incident does not map to a single CVE because it exploits no software vulnerability. The attack abuses documented, intentional auto-run features across four developer tools. The mechanism is a configuration injection: the attacker pushes files that conform to each tool's legitimate specification, triggering execution through designed behavior rather than a flaw.

Commit evasion: Commit 5f456b8 to Azure/durabletask used three layered evasion techniques. The [skip ci] flag in the commit message suppressed GitHub Actions pipeline execution. The author timestamp was set to 2020-03-09, placing the commit outside the window of recent-activity monitoring. No source files were modified; only configuration files were added, bypassing diff-based review heuristics.

Payload architecture: .github/setup.js is a single-line, 4,643,745-byte obfuscated JavaScript file. Deobfuscation uses a two-stage chain: ROT-N Caesar encoding wraps an AES-128-GCM encrypted second stage. The decrypted stage downloads the Bun runtime from github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-*.zip and re-executes outside the original Node process using child_process.spawn with detached: true, evading Node-based process monitors and surviving IDE restarts. The dropper is recompiled per wave, rotating hashes while preserving structure.

Credential collection: Six parallel collector classes target: AWS (~/.aws/credentials, environment variables), Azure (~/.azure/), GCP (~/.config/gcloud/), GitHub tokens (~/.gitconfig, environment), Kubernetes (~/.kube/config), HashiCorp Vault tokens, SSH keys (~/.ssh/), npm tokens (~/.npmrc), and local password managers (1Password, gopass, pass). The /proc/mem read bypasses GitHub Actions secret masking by accessing memory where secret values exist in unmasked form. This is the same technique documented in the TanStack compromise (CVE-2026-45321, CVSS 9.6) in May 2026.

Exfiltration: Stolen credentials are AES-256-GCM encrypted with a per-session key, RSA-OAEP wrapped, and uploaded via HTTPS POST to attacker-controlled dead-drop repositories following the path pattern {repo}/contents/results/results-{timestamp}.json. C2 beacon keyword in GitHub commit search: thebeautifulmarchoftime.

Propagation: The worm uses the GitHub createCommitOnBranch GraphQL mutation with stolen PATs to commit itself to every writable repository. These commits carry valid signatures because they are made through the GitHub API using legitimate tokens.

Endpoint protection check: Before executing, the malware checks for CrowdStrike, SentinelOne, Carbon Black, and StepSecurity Harden-Runner. It skips execution on Russian-language systems.

TECHNICAL EXECUTION CHAIN📝1Config files injected via PAT.claude, .gemini, .cursor, .vscode2Auto-run triggers on folder openSessionStart, folderOpen, alwaysApply🔓3ROT-N plus AES-128-GCM decodeTwo-stage chain decrypts second stage🏃4Bun runtime downloaded and spawnedDetached process evades Node monitoring🧠5/proc/mem read for secretsBypasses GitHub secret masking📤6AES-256-GCM plus RSA-OAEP exfilUploaded to dead-drop repos via HTTPSDropper recompiled per wave; hash rotation makes signature-based detection unreliable

CVE and Advisories

No CVE applies to the June 5 GitHub incident. The attack exploits documented auto-run features, not software vulnerabilities.

The related TanStack compromise is tracked as CVE-2026-45321 (CVSS 9.6), which documents the /proc/mem secret extraction technique used in both attacks.

The May 19 PyPI compromise of durabletask versions 1.4.1, 1.4.2, and 1.4.3 does not have an assigned CVE as of publication.

MITRE ATT&CK Mapping

Technique IDATT&CK nameHow it appeared
T1195.001Supply Chain Compromise: Compromise Software Dependencies and Development ToolsMalicious commit injected into Azure/durabletask via compromised contributor PAT
T1078Valid AccountsStolen PAT used to authenticate as a legitimate contributor across multiple operations
T1059.007Command and Scripting Interpreter: JavaScript4.6 MB obfuscated JavaScript payload executed via Bun runtime outside Node process
T1552.001Unsecured Credentials: Credentials in FilesHarvested AWS, Azure, GCP, SSH, npm, and Kubernetes credentials from filesystem paths
T1552.004Unsecured Credentials: Private KeysSSH private keys collected from ~/.ssh/
T1003OS Credential Dumping/proc/mem read to extract unmasked secrets from runner process memory
T1567.001Exfiltration Over Web Service: Exfiltration to Code RepositoryEncrypted credential dumps uploaded to attacker-controlled GitHub dead-drop repositories
T1053Scheduled Task/JobVS Code folderOpen task and AI agent SessionStart hooks used as execution triggers
T1036.007Masquerading: Double File ExtensionCommit backdated to 2020 and [skip ci] flag used to evade automated detection
T1543Create or Modify System ProcessPayload daemonizes via child_process.spawn with detached: true, surviving IDE restarts

Indicators of Compromise

Network Indicators

  • C2 domain (TeamPCP infrastructure): t.m-kosche[.]com
  • C2 domain (June 5 campaign): git-service[.]com (registered May 16, 2026)
  • Bun runtime download: github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-*.zip
  • Exfiltration pattern: HTTPS POST to {dead-drop-repo}/contents/results/results-{timestamp}.json

File Indicators

  • Malicious commit hash: 5f456b8 (Azure/durabletask)
  • Payload SHA256 (mantine-datatable wave): d630397d...873fdb8e (4,348,254 bytes)
  • Decrypted payload SHA256: 633c8410...1df5b64 (667 KB)
  • Payload path: .github/setup.js

GitHub Indicators

  • Dead-drop accounts: liuende501 (236 repositories), windy629 (200+ repositories), HerGomUli
  • Repository description patterns: "Miasma: The Spreading Blight", "Hades - The End for the Damned"
  • C2 beacon keyword in commit search: thebeautifulmarchoftime
  • Honeytoken name: IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner

Configuration File Indicators

Presence of any of the following files in a repository, particularly if added in a single commit with no source code changes, warrants immediate investigation:

  • .claude/settings.json with a SessionStart hook pointing to .github/setup.js
  • .gemini/settings.json with a SessionStart hook
  • .cursor/rules/setup.mdc with alwaysApply: true
  • .vscode/tasks.json with runOn: folderOpen
  • .github/setup.js (any large, single-line obfuscated JavaScript file)

Detection is complicated by hash rotation between waves. Behavioral detection (a process spawning Bun, or unexpected /proc/mem reads from an IDE process) is more reliable than signature matching.

Attribution

Attribution is assessed at medium confidence to TeamPCP, also tracked as Replicating Marauder, TGR-CRI-1135, UNC6780, DeadCatx3, PCPcat, ShellForce, and CanisterWorm. Google's Threat Intelligence Group formally tracks the actor under UNC6780 and characterizes it as financially motivated, specializing in supply chain attacks targeting open-source developer tooling and AI middleware.

Linking indicators: the C2 domain t.m-kosche[.]com in the May 19 PyPI payload is documented TeamPCP infrastructure; the same compromised contributor account was used in both the May 19 and June 5 attacks.

Complicating factors: TeamPCP open-sourced the full Mini Shai-Hulud toolkit on May 12, 2026, and advertised it on BreachForums. Wiz researchers note that code similarity alone is insufficient for definitive attribution given the public availability of the tooling. The Miasma campaign replaces Dune universe references with Greek mythology themes, consistent with a fork or adaptation. Copycat actors using the same public toolkit cannot be ruled out.

Behavioral note: the malware skips execution on Russian-language systems, consistent with prior Mini Shai-Hulud variants. No nation-state link has been established. TeamPCP has previously demanded $50,000 to $95,000 for stolen repository data sold on cybercrime forums.


Primary Sources