An attacker used to need infrastructure of their own — a server, a domain, a bit of botnet. Increasingly they do not. They use yours, or more precisely they use GitHub's, and they do it wearing the identity of a developer who stopped logging in three years ago.
I have been circling this since the spring and never finished the write-up. What made me pick it back up is that the separate stories — dormant accounts, ghost-account malware networks, hijacked CI runners — have stopped being curiosities and started looking like one supply line. They share a single insight: the cheapest thing to steal on a software platform is not code or credentials but trust and compute. GitHub supplies both in bulk, and both are hard to tell apart from ordinary developer noise.
Let me take the accounts first, then the runners.
The accounts that wait
In July, Datadog Security Labs published an account of several overlapping campaigns systematically mapping corporate GitHub organisations — enumerating repositories, members, followers, gists, starred projects — through the public API. The tooling was mundane: automated scrapers with user-agent strings like GitHub-Company-Scraper, GitHubAnalytics/1.5 and, my favourite for its honesty, repo-dumper.
What made it worth writing up was the identities behind the requests. More than fifty dormant "ghost" accounts, most created two to five years ago and left deliberately idle, woke up from around October 2025. Each ran for one to three weeks, then went quiet. Alongside them, dozens of legitimate accounts whose owners had leaked a personal access token or OAuth token — real developers, real history, borrowed for a fortnight.
The point of an aged account is that it does not look new. Julie Agnes Sparks of Datadog put it plainly: the operators lean on ghost accounts "that are often years old, or compromised OAuth tokens" precisely to pass the activity off as legitimate. A brand-new account querying every repository in your organisation trips a filter. An account with a 2021 join date, a plausible avatar and a handful of starred projects does not.
Most of this stayed at reconnaissance — building a map of who works where, which repositories exist, who follows whom. In at least one case it did not: a private repository was cloned outright, using a compromised token and a tool the operators had named repo-dumper. Reconnaissance is not the harmless end of the kill chain. It is the part that tells the attacker which door is worth forcing.
The same trick, industrialised
If Datadog's campaign shows dormant accounts used quietly, Check Point's Stargazers Ghost Network shows the loud version — the same account-aging tradecraft turned into a business.
Stargazers is a malware Distribution-as-a-Service, run by an operator Check Point calls Stargazer Goblin, advertised on criminal forums since 2023 and active since 2022. It runs on roughly three thousand ghost GitHub accounts. Their job is to make a malicious repository look legitimate: they star it, fork it, and subscribe to it, manufacturing the social proof that nudges a repository toward GitHub's trending lists and past a victim's instinct that "this project has 400 stars, it is probably fine."
The network is deliberately compartmentalised. One set of accounts serves the phishing template, another the images, another the malware release. When GitHub bans one — and it has removed more than 1,500 repositories since May 2024 — the others simply point to a fresh release and carry on. The payloads are the usual stealer catalogue: RedLine, Lumma, Rhadamanthys, RisePro, Atlantida. Check Point put the operator's takings north of $100,000, which is modest for the damage done and tells you how cheap the model is to run.
Two campaigns, one lesson: a GitHub account's credibility is a resource in its own right, and it can be aged, stolen, or mass-produced like any other.
The runners
Now the compute half — the part your brief called "gitrunner."
GitHub Actions runs CI/CD workflows either on GitHub's own machines or on self-hosted runners: servers you attach to a repository to do the building. Self-hosted runners are useful, and to an attacker they are a gift. They sit inside your network. They often hold cached credentials and secrets. And they exist to execute arbitrary code on command.
There are two ways this goes wrong. The first is that an attacker registers their own runner against a repository they have already got a foothold in. Sysdig documented this in the wake of the Shai-Hulud worm in November 2025, which backdoored more than 25,000 repositories. The pattern is tidy: request a runner registration token through the API, install the official GitHub runner binary with RUNNER_ALLOW_RUNASROOT=1, wire up a workflow that reads attacker-controlled text — a discussion comment — straight into a shell command, and set RUNNER_TRACKING_ID=0 so the spawned processes outlive the job that "finished." The runner becomes a backdoor wearing a CI badge.
The second way is subtler, older, and the one that reaches your customers. Praetorian's TensorFlow write-up is the cleanest example. TensorFlow ran a persistent, self-hosted runner for its ARM64 builds. The runner was non-ephemeral — it kept state between jobs — and workflow approval was not required for pull requests from anyone who had contributed before. So the attack was: fix a typo to become a "contributor," then submit a pull request whose workflow quietly pulls and runs a payload.
From there you hold the runner's GITHUB_TOKEN with write access to a repository with 180,000 stars, the AWS token it uses to publish its pip packages, and the ability to tamper with releases that ship to half the industry. Google paid it out as a critical supply-chain finding and fixed it the only sensible way: require approval on all fork pull requests, and make the token read-only on those builds.
You do not even need a self-hosted runner to be caught by this class of bug. In March 2025 the widely used tj-actions/changed-files action was compromised — CVE-2025-30066 — and rewritten to dump CI secrets into build logs across more than 23,000 repositories. One trusted dependency in the pipeline, pinned to a mutable tag, and everyone who used it inherited the compromise in a single push. CISA issued an alert. The initial foothold, fittingly, came through yet another compromised action.
Why these belong in one article
Strip the detail away and the shape is identical across all of it.
Attackers have stopped bringing their own infrastructure where they can borrow yours. A dormant account is rented identity. A hijacked runner is rented compute. A poisoned action is rented trust in the supply chain. In every case the malicious activity is camouflaged inside the ordinary traffic of software development — API calls, stars, pull requests, CI jobs — which is precisely the traffic your monitoring is trained to wave through.
It is the same lesson as the last thing I wrote here, seen from the other side of the table. There, a crew left its own server open on the internet and got caught. Here, the attackers have worked out that they never needed a server of their own in the first place.
What to actually do
Split by who is reading, as usual.
If you sit on a board: your source code and your build pipeline are a named target now, not a developer's private concern. Three questions are worth asking out loud. Do we know which of our GitHub identities can be impersonated. Do we know what our CI runners can reach if one is taken over. And would we notice a private repository being cloned. If the answer to the last one is "no," it is the same finding it has been in every breach of the past decade — nobody was watching the quiet door.
If you run security: turn on the controls GitHub already gives you. Make personal access tokens fine-grained and short-lived; classic PATs with no expiry are the currency these campaigns run on. Require review for workflows triggered by fork pull requests. Prefer ephemeral, just-in-time runners that are destroyed after each job, and never attach a self-hosted runner to a public repository. Pin actions to a commit SHA, not a tag — a tag is mutable, and tj-actions is what a mutable tag costs. And treat one identity enumerating your whole organisation as an event worth an alert: the user-agents in the Datadog report are a starting signature, but the behaviour is the real tell.
If you are the one at the console: rotate the token you forgot you issued. The compromised-but-legitimate accounts in these campaigns belong to real developers who leaked a PAT into a config file, a container image, or a public gist, and never revoked it. Your abandoned tokens are somebody else's aged account. Audit what you have out there, scope it down, and give it an expiry. A credential that never expires is not a convenience — it is a dormant account waiting to happen.
The uncomfortable part is that none of the defences are exotic. Short-lived tokens, ephemeral runners, pinned dependencies, and someone actually reading the API logs would blunt every campaign above. It is the usual shape: the attacks are industrialised and patient, and the controls that stop them are dull, well-documented, and mostly switched off.
I put this research down in the spring because it felt like a set of interesting one-offs. It is not one-offs any more. The accounts are already aged. The runners are already attached. The only variable left is whether anyone on your side is looking.