GhostAction Opens a Second Front: a Fake Font, a VS Code Trigger, and an OAuth Approval the Victim Never Sees
GhostAction has stopped being only a CI problem. In a deep dive published 16 October 2026, the OpenSourceMalware team documents a second stage that persists on developer machines even after the malicious workflow is removed: a hidden VS Code task plus a credential stealer disguised as a font file, found across a 94-repository cluster. And in parallel, reporting on 19 October details a phishing arm that steals maintainer accounts through a reverse proxy and completes GitHub’s OAuth device flow automatically inside the victim’s own authenticated session — the victim types no code and clicks no approval. This is the fourth installment of a campaign we have tracked since early October — from the September wave through the history-sweeping October burst to the 30,000-repository final accounting — and it is the first installment where the target moved off the CI runner and onto the developer’s laptop.
The launcher: routine linting that runs when the folder opens
Stage 2 is three files in the repository and one scheduled job on the victim’s machine. The launcher is a .vscode/tasks.json containing a single task labelled eslint-check — the kind of name that ends an investigation before it starts — with runOn: folderOpen as the trigger, so VS Code executes it automatically whenever anyone opens the folder. Presentation settings keep it out of sight: never reveal, dedicated panel, no focus, no echo, auto-close. OpenSourceMalware reports the file is byte-identical in all 79 repositories it analysed, the signature of a operator working from a template rather than hand-crafting per-target lures. The task’s command is node ./public/fonts/fa-solid-400.woff2 — and Node.js does not care about the extension, so the “font” runs.
The choice of filename is deliberate tradecraft, and the attribution handling around it is refreshingly careful. fa-solid-400.woff2 is the exact filename used by North Korea’s Fake Font/PolinRider campaign, and the cluster was found by Jamf Threat Labs researcher Thijs Xhaflaire during a routine hunt for new PolinRider victims. But OpenSourceMalware explicitly labels this a copycat rather than an extension of PolinRider: the fake font here holds 38 lines of plain, unobfuscated JavaScript rather than the DPRK tooling’s payloads, the exfiltration IP matches GhostAction’s known 193.32.204.199, and the team states there are no shared IOCs with any other DPRK malware it has seen. Given thousands of live PolinRider samples sitting on GitHub to crib from, imitation was the path of least resistance — and the operator took it. Resist the urge, visible in some secondary coverage, to upgrade “same filename” into “same actor.”
The stealer: 38 lines aimed at the agent credential store
The payload’s shopping list is what earns this briefing its place on this site. Alongside the expected haul — .aws/credentials, .pypirc, .npmrc, .git-credentials, .netrc, .kube/config, both SSH private keys — the stealer reads .claude/.credentials.json and .gemini/oauth_creds.json: the local credential stores of Anthropic’s and Google’s coding agents. A grep stage then sweeps the home directory for AKIA/ASIA key IDs, sk-ant- tokens, ghp_, npm_ and PyPI tokens. This is the same credential class as the agent-state stealer shipped in the hijacked ovhcloud-cli releases we covered earlier this month — the industry keeps learning, machine by machine, that the files AI agents leave on disk are now high-value targets, and the malware keeps arriving before the file permissions do.
Persistence is a cron job that runs attacker-supplied code every five minutes, and exfiltration goes to the same server over plain HTTP on port 8080. A second GitHub Actions workflow — cycling through plausible names like lint.yml, ci-check.yml, build-check.yml, repo-health.yml, audit.yml and code-review.yml — acts as the fallback if the infostealer never runs, with added credential formats and a fallback upload to a secret gist if the POST fails. The architecture assumes its own takedown: remove the workflow and the laptop still reports in; clean the laptop and the workflow re-seeds. Either half alone is an incident; together they are a foothold designed to survive the obvious remediation.
The other arm: a phishing page that approves its own OAuth grant
The account-acquisition side evolved in step. The operator registered my-github[.]com on 9 October — seventeen days after the similarly named my-gitlab[.]com — pointed it at the same 193.32.204.199 exfiltration endpoint, and the same day sent a phishing email from noreply@my-github[.]com impersonating a GitHub security alert about a suspicious Moscow login. The linked page is a reverse proxy harvesting the password and 2FA code, with injected JavaScript that authorises an OAuth app carrying repo, workflow and read:org scopes — repository read/write, workflow-file modification, and organisation visibility, precisely the permissions GhostAction’s workflow injection needs.
The notable mechanism, detailed by researcher Paul McCarty, is that the kit automates GitHub’s OAuth device flow through the victim’s proxied session: it receives a device code and user code, loads /login/device, extracts GitHub’s own forms from the returned HTML, inserts the user code, and posts the approval with authorize=1 — all using the victim’s authenticated session. The Stolen token lands at my-github[.]com on TCP/8443, and the victim never types a code or consciously approves anything. This is device-code phishing with the human approval step compiled out, and defenders should note what it defeats: the 2FA was already harvested by the proxy, and the OAuth consent screen — the control users are trained to scrutinise — never renders for a human at all. Training that says “check what you approve” has nothing to attach to when the approval happens in a form the user never sees.
One caution on infrastructure reading: McCarty explicitly states there is no evidence tying my-github[.]com to my-gitlab[.]com beyond the naming pattern — different registrars, registrant records, Cloudflare nameserver pairs, IP addresses, ASNs and DNS designs, with no shared certificate or account artifact. Same-name-means-same-actor is exactly the inference error the PolinRider filename invites, and the researchers are declining it in both cases. Follow their example.
What to do
- Hunt the three files, not just the workflow. Search your GitHub estate for
.vscode/tasks.jsonfiles containingfolderOpenpaired with apublic/fonts/*.woff2path, and for the fallback workflow filenames (lint.yml,ci-check.yml,build-check.yml,repo-health.yml,audit.yml,code-review.yml,test-suite.yml,cache-cleanup.yml) appearing in commits you did not author. The file’s byte-identical template makes it greppable. - Any developer who opened an affected folder is a full workstation compromise, not a repository incident: the stealer exfiltrates on open and installs a five-minute cron job. Rotate every credential class in its shopping list — AWS, PyPI, npm, Git, Kubernetes, SSH, and critically the
.claudeand.geminiagent credentials — then rebuild or reimage the machine. Credential rotation without removing the cron job is theatre. - Audit OAuth grants and device-flow authorisations org-wide. Look for third-party OAuth applications authorised around or after 9 October with
repoplusworkflowscopes that nobody recognises, revoke them, and review what those tokens could have touched — repository enumeration plus workflow injection is the GhostAction playbook. - Block and hunt the infrastructure:
my-github[.]com(and treat lookalike code-host domains with suspicion),193.32.204.199including ports 8080 and 8443 in egress logs, and brief developers that “suspicious login from Moscow” security-alert lures are the current acquisition vector. - Lock down agent credential files now, before your incident.
~/.claude/.credentials.json,~/.gemini/oauth_creds.jsonand equivalents should be0600, excluded from backups and dotfile repos, and covered by secret-scanning — the ovhcloud-cli stealer wanted the same files, which means this target set is now standard across unrelated malware families.
Our verification was read-only and external. The 16 October 2026 OpenSourceMalware deep dive was read first-hand: the 94-repository cluster figure, the Thijs Xhaflaire (Jamf Threat Labs) discovery via a PolinRider hunt, the three-file stage plus cron job, the byte-identical tasks.json across 79 analysed repositories, the eslint-check label and folderOpen trigger, the 38-line plain-JavaScript payload and its file and credential target lists (including .claude and .gemini stores), the five-minute cron cadence, the plain-HTTP port-8080 exfiltration, the fallback workflow filenames and gist fallback, and the explicit copycat-not-extension attribution judgment are all from that primary post. The my-github[.]com registration date, Moscow-login lure, reverse-proxy 2FA harvest, automated device-flow approval mechanics, repo/workflow/read:org scopes, TCP/8443 exfiltration, the 94-repositories/66-owners accounting and the my-gitlab[.]com non-attribution were read from The Hacker News’ 19 October report (Ravie Lakshmanan) and corroborating secondary coverage and are attributed accordingly. No repository was cloned, no payload was executed, and no attacker infrastructure was contacted. Brave Search was used for discovery only, and payload contents are summarised at target-list level, not reproduced.
Sources:
- OpenSourceMalware — “GhostAction Copycats PolinRider by Hiding a Stealer in a Fake Font File” (16 October 2026; 94-repo cluster, Thijs Xhaflaire discovery, tasks.json launcher, 38-line woff2 stealer, five-minute cron, fallback workflow, copycat attribution)
- The Hacker News — “GhostAction Attackers Plant Credential Stealer Disguised as Font in 94 GitHub Repositories” (Ravie Lakshmanan, 19 October 2026; my-github[.]com phishing, automated OAuth device-flow approval, fallback workflow names)
- al-ice.ai — GhostAction’s October Burst, Fully Counted: 30,000 Repos, Three Payload Versions, and the Runs GitHub Stopped
- al-ice.ai — GhostAction Learned to Read Git History — the October Burst Sweeps the Whole Tree for Cloud and AI Keys
- al-ice.ai — related agent-credential-store targeting (ovhcloud-cli release hijack)