ghtier

IRL incidents

Almost every incident below is a story about which permission the stolen credential carried, not which product had a bug. They're organized by the same four access planes as the catalog. Switch tabs to see just one plane, or scroll down for all of them plus the recurring escalation primitives that keep reappearing across incidents. For the same incidents broken down by entry vector and objective, with the defense for each step, see Attack paths.

Timeline

Hover or tap an event for a quick preview, then click through for the full writeup.

↻Loops back to the start
↻Loops back to the start

Recurring escalation primitives

If you're tiering by blast radius, these are the actions that keep showing up as the pivot point, roughly in order of how often they appear below.

PrimitivePermission behind itSeen in
Force-repoint version tagscontents:write on an Action repotj-actions, TeamPCP
Inject a workflow fileworkflow scope / contents:writeDependabot spoof, GhostAction, Shai-Hulud, Salesloft
Flip repo visibility to publicorg owner / repo adminToptal, Nx phase 2, Shai-Hulud
Steal GITHUB_TOKEN from runner memory or .git/configany RCE on a runnertj-actions, PyTorch, ArtiPACKED
workflow_dispatch a privileged publish jobactions:writeNx
Register a self-hosted runneradmin:org / repo adminShai-Hulud 2.0
Non-ephemeral runner persistencerunner config defaultPyTorch, TensorFlow
Poison the shared Actions cachefork PR write into shared cacheUltralytics, Angular, TanStack
Add a guest/outside collaboratororg ownerSalesloft
Forge a SAML assertionnone (pre-auth)CVE-2024-4985

Two things stand out for the severity model. First, contents:write on a widely consumed repo is categorically different from the same permission on a leaf repo, because tag mutation propagates to every downstream consumer pinning by tag. Second, several of the worst incidents involved no vulnerability at all; cache sharing, non-ephemeral runners, and agent token breadth are all designed behaviour, which is why severity here needs a "works as intended" flag distinct from CVE-backed findings.

User plane

Individual identity & personal profile

July 2023

Dependabot impersonation

Attackers used stolen personal access tokens to push commits faked to look like automated Dependabot contributions across hundreds of public and private repositories. They added a hook.yml workflow file that shipped GitHub secrets to an external server on every push, and appended obfuscated JS to every .js file in the project to harvest form passwords.

Click to open →
January 2024

Mercedes-Benz source code exposure

An employee committed a token to a public repo. It gave unrestricted and unmonitored access to the entire source code on the internal GitHub Enterprise Server, exposing database connection strings, cloud access keys, blueprints, design documents, SSO passwords, and API keys. The first token granted full ownership of the account; inside one of its private repos sat a second token to the GHE instance, a classic chained-credential blast radius that ran from the user plane straight into the enterprise plane.

Click to open →
2025–2026

OAuth consent phishing at scale

A campaign hit roughly 12,000 repositories with fake "Security Alert" issues, tricking developers into authorizing a malicious OAuth app that then held full control of their accounts and code. Because the link lands on the real GitHub authorization screen, it passes SPF/DKIM, bypasses MFA, and grants a persistent token without ever touching a password. Device-code phishing against GitHub follows the same shape: research reports success rates above 90% in authorized engagements, and a broadly scoped phished token yields private repos, Actions secrets, and more.

Click to open →
December 2022 – January 2023

CircleCI session-cookie theft

Malware on an engineer's laptop stole a 2FA-backed session cookie, letting the attacker impersonate an employee who had rights to generate production tokens. Customer environment variables, tokens, and SSH keys were exfiltrated, and encryption keys made at-rest encryption moot. CircleCI rotated every customer GitHub OAuth token on their behalf. Worth noting for tiering purposes: a session cookie sat above MFA in the trust chain.

Click to open →
May 2026

GitHub's internal repositories taken through a VS Code extension

On 18 May 2026 GitHub detected and contained the compromise of an employee's device through a poisoned VS Code extension from a third party. Aikido reports that GitHub confirmed the extension as Nx Console, which has more than 2.2 million installs and a verified publisher; the bad release was pulled from the VS Code Marketplace within about 18 minutes. That was enough. The attacker claimed about 3,800 repositories, and GitHub said the claim was directionally consistent with its own investigation. Only GitHub's internal repositories were affected, but GitHub rotated the GitHub Enterprise Server signing key as a precaution, and every GHES customer had to rotate the GPG public key used to verify upgrades. Nobody needed a GitHub permission to start this: the extension ran with the developer's own access.

Click to open →
October 2025 – March 2026

GlassWorm: editor extensions that steal GitHub tokens and force-push

GlassWorm started on 17 October 2025 as malicious VS Code extensions on Open VSX and the VS Code Marketplace. The loader is written in invisible Unicode characters, so it shows as blank space in editors, terminals and GitHub's diff view. Once installed, it takes GitHub, Git, npm and Open VSX credentials from the developer's machine, uses them to infect more extensions, and turns the machine into a proxy with remote access. From late 2025 the stolen GitHub tokens were also used against repositories. In March 2026 the same actor took the latest real commit in hundreds of Python repositories, added an obfuscated payload to files such as setup.py, and force-pushed it to the default branch. The author and commit message were kept, so only the committer date gives it away. Aikido found at least 151 affected repositories through code search in 3 to 9 March, and StepSecurity found hundreds more across hundreds of accounts, which it tracks as ForceMemo and ties to GlassWorm through the same command wallet. A force-push like this only works where the branch allows it.

Click to open →

Organization plane

Team collaboration & codebase ownership

Ongoing

"Pwn requests": pull_request_target & workflow_run

The structural flaw behind most of the incidents on this page. pull_request deliberately withholds write permissions and secrets; pull_request_target was created to restore them, and becomes hazardous the moment maintainers treat it as a normal CI trigger. workflow_run carries the same risk and gets far less attention: the triggered workflow can access secrets and write tokens even when the workflow that triggered it could not. One industry scan found 38% of organizations have a workflow vulnerable to script injection or dangerous triggers. Since 8 December 2025, pull_request_target always runs the workflow file from the default branch and checks out the default branch, whatever branch the pull request targets. That closes attacks through an old, vulnerable workflow left on another branch. It does not help when the default branch workflow itself checks out and runs the pull request's code.

Click to open →
December 2024

Ultralytics build cache poisoning

Template injection via a crafted branch name against a format.yml workflow using pull_request_target. Secrets including secrets._GITHUB_TOKEN were exfiltrated and the build cache poisoned to inject a cryptominer into v8.3.41 and v8.3.42. The read from researchers was that the attackers had write access but preferred extracting CI/CD secrets, then used them to poison the cache and ship malicious releases.

Click to open →
August 2025

Nx "s1ngularity"

The injected script extracted a read/write GitHub token, a consequence of the workflow permissions setting, then created a branch replacing the CI script, triggered the publish workflow against that branch via the API using workflow_dispatch, and deleted branches and workflow runs to clean up. When publish ran, it had NPM_TOKEN in scope and exfiltrated it. The PR targeted an outdated branch that still carried the vulnerable workflow after it had been reverted on master. In phase two, the stolen GitHub tokens were used to flip thousands of private repositories to public. Downstream, one threat-intel group rode the consequences to full AWS administrator access at a victim org in under 72 hours, via OIDC trust chains and a CloudFormation IAM capability chain. No zero-days were involved.

Click to open →
2023–2024

Self-hosted runner takeovers (PyTorch and others)

A fork PR makes you a "previous contributor," after which workflows run on the self-hosted runner without approval; default runner setup is non-ephemeral, so access persists. On PyTorch, researchers confirmed root on the runners and pulled GITHUB_TOKENs with extensive write access out of .git/config during active workflows. Against GitHub's own actions/runner-images, the access reached internal macOS private cloud vCenter and Azure credentials, and could have poisoned every hosted runner base image. TensorFlow's fix was requiring approval for all fork PRs and setting GITHUB_TOKEN to read-only on self-hosted runners.

Click to open →
September 2025, returned 2026

GhostAction

327 compromised GitHub user accounts were used to inject workflows disguised as "Add GitHub Actions Security workflow" across 817 repositories, stealing 3,325 secrets. The actors enumerated secret names from legitimate workflow files first, then hardcoded those names into their own workflow, harvesting PyPI, npm, DockerHub, GitHub, Cloudflare, and AWS credentials. It came back in 2026. GitGuardian found the malicious workflow in 772 public repositories belonging to 373 users and organizations between 31 August and 30 September 2026, aimed at 2,577 secrets; GitHub held most of the runs for approval, and by 5 October only 16% of the repositories had removed the workflow. From 7 October Socket saw more than 500 accounts committing it to tens of thousands of repositories.

Click to open →
July 2025

Toptal repo-visibility flip and wiper

73 repositories in the org went public within a five-minute window; at least 10 contained code to exfiltrate GitHub auth tokens and destroy victim systems. The payloads sat in preinstall/postinstall hooks, sending the token to a webhook endpoint then recursively deleting files on Windows and Linux. About 5,000 downloads occurred before removal. This was an org-owner compromise, and the very first action taken was flipping repo visibility.

Click to open →
March – June 2025

Salesloft Drift GitHub → AWS pivot

Investigators found the threat actor UNC6395 in the Salesloft GitHub account from March through June 2025, downloading content from multiple repositories, adding a guest user, and establishing workflows. They then moved to Drift's AWS environment, took OAuth tokens for customer integrations, and used those to reach customer data. Downstream victims included Zscaler, Palo Alto Networks, Cloudflare, PagerDuty, and others. The interesting detail for the tier model: "add a guest user" and "establish workflows" are both persistence primitives that org admin grants.

Click to open →
September 2025 / November 2025 / May 2026

Shai-Hulud self-propagating worm (1.0 & 2.0)

The worm made GitHub permissions the propagation mechanism. It validated available GitHub tokens and their permissions, then queried /user/repos?affiliation=owner,collaborator,organization_member to find repos where the compromised account had enough privilege. It planted shai-hulud-workflow.yml, exfiltrated ${{ toJSON(secrets) }} to a webhook, and made repos public, including any the victim merely held tokens for. The 2.0 variant registered the infected host as a self-hosted Actions runner named SHA1HULUD and planted an injection-prone workflow at .github/workflows/discussion.yaml, so opening a GitHub Discussion gave arbitrary command execution on the host. 2.0 compromised 796 npm packages, with credential exfiltration confirmed across more than 500 GitHub users spanning 150+ organizations. A smaller descendant, known as Mini Shai-Hulud, hit in May 2026: on 19 May one developer account takeover led to more than 630 malicious versions across 317 npm packages in about 20 minutes, including AntV packages. The TanStack compromise a week earlier came through a different door, Actions cache poisoning (see the cache poisoning incident).

Click to open →
February 2026

hackerbot-claw autonomous agent RCE

An autonomous agent doing this at scale. It abused unsafe pull_request_target, unsanitized inputs, dynamic shell execution, and overprivileged GITHUB_TOKEN to get RCE on GitHub-hosted runners across at least six repositories, exfiltrating write-capable tokens in some cases. Targets included Trivy, avelino/awesome-go, microsoft/ai-discovery-agent, DataDog/datadog-iac-scanner, project-akri/akri, RustPython, and ambient-code/platform, where the vector was prompt injection via CLAUDE.md, which was blocked. On Trivy it stole a PAT through a pull_request_target workflow and used it to take over the repository.

Click to open →
March – April 2026

TeamPCP tag force-overwrite campaign

The most complete demonstration of tag-write as a permission tier. On 19 March 2026 (CVE-2026-33634), continuing an attack that began in late February, the actors used credentials with tag-write privileges to publish Trivy v0.69.4 and force-overwrite 76 of 77 tags in aquasecurity/trivy-action and all seven in aquasecurity/setup-trivy, redirecting trusted versions to malicious commits with no visible change in release metadata. Checkmarx's KICS GitHub Action followed on 23 March, when all 35 of its tags were moved through a compromised service account, and on 24 March LiteLLM versions 1.82.7 and 1.82.8 were uploaded straight to PyPI with credentials taken from LiteLLM's own CI, where Trivy ran. Researchers traced at least seven waves through to @bitwarden/cli hijacked with stolen KICS npm tokens, and elementary-data via Actions script injection. Each compromised environment yielded the credentials for the next.

Click to open →
Ongoing

Actions cache poisoning (Angular, TanStack)

Actions caches are shared between PR workflows and release workflows. One researcher's angular/dev-infra chain was script injection via ${{ github.head_ref }} in a pull_request_target workflow, then cache filling with a purpose-built tool. TanStack saw 84 malicious versions across 42 @tanstack/* packages from the same class of chain on 11 May 2026: a pull_request_target workflow ran a fork's code, which poisoned the pnpm store in the shared cache, and the release workflow restored it and leaked its npm OIDC token. There is no CVE for any of this; the cache is behaving exactly as designed.

Click to open →
March – April 2026

prt-scan: automated pull_request_target attacks

The second AI-assisted campaign of 2026 against pull_request_target, after hackerbot-claw. Wiz traced more than 500 malicious pull requests in six waves from six accounts, starting on 11 March 2026. The largest burst came from one account that opened more than 475 pull requests in 26 hours from 2 April. Early waves used crude shell scripts; later ones used AI-generated payloads written for each repository's language, including a stage that tried to get past label-gated workflows. Fewer than one in ten attempts worked, but at least two npm packages were published with the stolen credentials. The attacker never needed access to a target repository, only a fork and a workflow that ran its code with secrets.

Click to open →
May 2026

Megalodon: mass backdooring of CI workflows

On 18 May 2026, in about six hours, throwaway accounts pushed 5,718 commits into 5,561 public repositories, straight to the default branch and without pull requests. The commits claimed to come from bots such as build-bot and ci-bot, but no real account was linked to those names. A mass variant added a workflow that ran on push and pull_request_target and sent secrets out. A targeted variant replaced existing workflows with one that only runs on workflow_dispatch, a dormant backdoor the attacker can fire later through the API. At least one project, Tiledesk, then published npm versions built from the poisoned source; the npm account itself was never touched. How the attacker got push access to so many repositories is not established in the primary write-ups.

Click to open →
August 2026

ChainDrop: a hijacked maintainer account publishes through trusted publishing

On 4 August 2026 an attacker used the GitHub account of the maintainer of keyv and cacheable, packages with well over 100 million weekly npm downloads together. The commits went straight to main, unsigned, which StepSecurity reads as a stolen token or session used through the API; how the account was taken is not known. The attacker then cut releases, and the projects' own release workflows published the malware through npm trusted publishing, with valid provenance. No npm token was stolen for this part. The payload, a descendant of Shai-Hulud 2.0, steals npm and GitHub tokens, cloud keys and SSH keys from developer machines and CI runners, publishes itself to every package the victim can publish, and in victims' repositories adds a workflow that dumps the secrets context, then deletes the branch. It also writes .vscode/tasks.json and .claude/settings.json into repositories to run again later. StepSecurity counted 2,212 versions of 444 packages within about four hours. Provenance showed which commit was built, not whether anyone should have pushed it.

Click to open →

Enterprise plane

Corporate policy, licensing, and compliance

Fixed 2024

CVE-2024-4985: SAML auth bypass

Rated CVSS 10.0. With SAML SSO and encrypted assertions enabled, an attacker could forge a SAML response to provision or access a site administrator account, with no prior authentication required. All GHES versions before 3.13.0 were affected; fixed in 3.9.15, 3.10.12, 3.11.10, and 3.12.4. Site admin means every repository, secret, and CI/CD pipeline on the instance. A single forged assertion reaches Tier 0 with zero credentials.

Click to open →
Fixed 2025

CVE-2025-11578: pre-receive hook symlink escape

An authenticated enterprise admin could escape via symlink in pre-receive hook environments, replace system binaries during hook cleanup, and add an SSH key to root's authorized_keys, turning enterprise-admin access into root on the appliance itself.

Click to open →
Fixed 2025

CVE-2025-3509: pre-receive hook RCE via port reuse

Pre-receive hook RCE via dynamically allocated ports available during operations such as hot-patch upgrades, requiring either site admin or a user able to modify repos with hooks already enabled.

Click to open →
Fixed 2026

CVE-2026-3854: push-option metadata RCE

Rated CVSS 8.7. A low-privileged user with push access to any repository, including one they created themselves, could inject metadata fields through crafted git push option values and achieve RCE on the instance. Notable because the starting privilege required is minimal: ordinary repo push, not admin.

Click to open →
Fixed 2024

CVE-2024-1374: Management Console template injection

A Management Console editor reached root SSH on the appliance via Nomad template injection in the audit log forwarding configuration, a mid-tier admin console role escalating straight to host-level control.

Click to open →
Fixed 2022

CVE-2022-23732: Management Console path traversal / CSRF bypass

A Management Console path traversal vulnerability bypassed CSRF protections, leading to privilege escalation on the appliance.

Click to open →

Application plane

Secure machine-to-machine integrations

April 2022

Heroku / Travis CI OAuth token theft

The canonical application-scope incident. Stolen OAuth user tokens issued to Heroku and Travis CI were used to authenticate to the GitHub API and download private repositories from dozens of organizations, including npm; GitHub assessed the actor was likely mining those repos for secrets to pivot into other infrastructure. Detection started when GitHub found unauthorized access to npm production infrastructure using an AWS key that had been lifted from a private npm repo downloaded with one of those tokens. Five specific app IDs were implicated (three Heroku Dashboard variants, Heroku Dashboard Classic, and Travis CI). The tokens were never compromised at GitHub. The app vendor was the weak point.

Click to open →
March 2025

tj-actions/changed-files supply-chain compromise (CVE-2025-30066)

The attacker used a previously obtained GitHub token with write permissions to push a commit disguised as renovate[bot], slipped into a legitimate auto-merged PR, then retroactively repointed multiple version tags at the malicious commit, affecting over 23,000 repositories. The payload dumped runner memory to logs, exposing access keys, PATs, npm tokens, and private RSA keys. Upstream, reviewdog/action-setup (CVE-2025-30154) was backdoored first, and researchers traced the original target to Coinbase's agentkit repo. The permission abused, contents:write on an Action repo, implies tag-write, which implies control of every consumer pinning by tag.

Click to open →
August 2024

ArtiPACKED artifact token leakage

actions/checkout persists the GitHub token into .git/config by default; uploading the whole checkout directory as an artifact exposes it. Researchers observed leakage of both GITHUB_TOKEN and the undocumented ACTIONS_RUNTIME_TOKEN, which lives about six hours and can be used to swap an artifact for a malicious version. The token-theft path is a race condition: download the artifact mid-run and create a branch before expiry. It requires the workflow to hold contents: write.

Click to open →
May 2025 – ongoing

GitHub MCP / agentic token breadth

Invariant Labs showed a malicious public issue can coerce an agent into pulling private repo data into context and leaking it via an autonomously created public PR, arguing this can't be fixed server-side and needs architectural controls. The pattern kept spreading: Orca's RoguePilot used a hidden prompt in an issue to make Copilot leak a repository's privileged token, and Noma's GitLost targeted agentic workflows handed a token with read access across an org's repos, private ones included. This is the clearest case where the token's scope breadth is the entire vulnerability. Nothing else needs to go wrong.

Click to open →
Ongoing

GitHub App installation token abuse

An attacker holding the App private key, or landing on a runner that carries the installation, mints fresh short-lived tokens with scopes like contents:write and pull_requests:write, then reads private repos, pushes code, or opens malicious PRs. GHES also had an incorrect-authorization bug, fixed in 2022 (versions 3.3.17/3.4.12/3.5.9/3.6.5), where a scoped user-to-server token could escalate to full admin/owner.

Click to open →