Hacking

GhostAction Phishing: Fake Fonts and Stolen OAuth

Published  ·  9 min read

Your repository might look clean and still be compromised, because the actor behind GhostAction has moved past injecting workflows entirely, and the new GhostAction phishing campaign targets developers directly through a fake GitHub security alert, steals their password and 2FA code, quietly authorizes an OAuth app with workflow-modification rights, and leaves behind a stealer hidden inside a font file that keeps running long after the workflow gets deleted.

OpenSourceMalware detailed the shift, and the timing tells its own story. GitHub started blocking the original workflow on October 9. The persistence layer arrived six days later.

What Makes This GhostAction Phishing Campaign Different

The first wave was broad and automated. Compromised maintainer tokens, hundreds of repositories, a workflow that ran on push and harvested secrets.

This wave is narrow and human. It's social engineering aimed at specific people, wrapped around a technical payload that survives cleanup.

Detail

Value

Campaign

GhostAction, stage 2

Discovery

OpenSourceMalware

Phishing domain

my-github[.]com

Domain registered

October 9, 2026

Resolves to

193.32.204[.]199

Token exfiltration

TCP/8443

Stealer exfiltration

Plain HTTP, port 8080

Repositories with stage 2

94

Owners affected

66

Owners deleted, renamed, or private

15

That last row matters. A sixth of the affected owners responded by removing or hiding their repositories. The rest are still out there.

The my-github[.]com Domain and the IP It Shares

The domain was registered on October 9, 2026, and pointed at 193.32.204[.]199. That's the same address used as the exfiltration endpoint for data harvested by the original malicious workflow.

The registration came 17 days after my-gitlab[.]com went live. Same naming pattern, different code host.

That similarity is a tempting pivot, and Paul McCarty was careful about it. His assessment is that the infrastructure doesn't directly connect them.

Different registrars. Different registrant records. Different Cloudflare nameserver pairs, IP addresses, ASNs, and DNS designs. No shared certificate or account artifact recovered.

A naming convention is a lead, not a link.

How the Phishing Page Actually Works

The email arrived on the day the domain was registered, sent from noreply@my-github[.]com.

It impersonated a GitHub security alert and warned of a suspicious new login from Moscow, Russia. It told the recipient to click through and secure their account.

The page behind that link was a reverse proxy sitting in front of the real GitHub login. That's the whole trick, and it explains why the credential theft works at all.

What the Kit Collects

The proxy harvests the victim's password and their two-factor authentication code. Both, in real time, through a live authenticated session rather than a static clone.

Then the JavaScript goes further. It authorizes an OAuth app with permissions to modify the victim's workflows.

The Device Flow Automation

The page also silently authorizes a GitHub OAuth device flow, requesting repo, workflow, and read:org scopes.

McCarty walked through the mechanics. The script receives a device code and a user code, loads /login/device, extracts GitHub's forms from the returned HTML, and submits them automatically. It inserts the user code, finds the authorization form, adds authorize=1, and posts the approval through the victim's authenticated proxied session.

The victim never types a device code. They never consciously approve the application.

That's device code phishing with the hardest part automated away. The stolen token gets exfiltrated to my-github[.]com over TCP/8443.

Why the OAuth Scopes Are the Real Payload

A stolen password is a problem. A stolen repository-scoped OAuth token is a durable one.

The token is a reusable API credential. It doesn't expire when the victim changes their password in a panic, and it doesn't require them to be online.

The granted scopes are consistent with what GhostAction needs to work at scale. Read and write repositories. Modify Actions workflow files. Read organization membership and related organizational data.

That's repository enumeration plus workflow injection, using an access path GitHub's workflow block doesn't touch.

Put plainly, the operator can now combine repository-scale automation with individual social engineering against people whose accounts reach projects, organizations, registries, and everyone downstream of them.

Stage 2: Persistence That Outlives the Workflow

Here's the part that changes how you respond to a GhostAction finding.

The campaign now commits three separate files, and they're designed so that removing the malicious workflow isn't the end of the story.

File

Purpose

.vscode/tasks.json

Runs automatically when the folder opens

public/fonts/fa-solid-400.woff2

Contains the credential stealer payload

A second workflow file

Activates if the stealer never runs

The pattern borrows from North Korea's playbook. The font filename itself is the same one used in the Fake Font / PolinRider campaign from January 2026.

The VS Code tasks.json Trigger

This is the cleverest piece, and the one most likely to catch a developer who thinks they've cleaned up.

The tasks.json file carries a runOn: folderOpen trigger. Open the folder in VS Code, and the task executes. No build, no test run, no deliberate action.

That task auto-runs the credential stealer. Alongside collecting data, it installs a cron job that executes attacker-supplied code every five minutes.

Collected data goes to the same server over plain HTTP on port 8080.

What the Fake Font Steals

The payload inside fa-solid-400.woff2 is built for a developer's machine, which means it's built for everything on it:

  • AWS keys
  • PyPI tokens
  • npm tokens
  • Git logins
  • Kubernetes cluster credentials
  • SSH private keys
  • Anthropic Claude and Google Gemini credentials

That list is a map of a modern development environment's blast radius. Any one of those items is enough to extend the compromise well past the machine.

The Fallback Workflow Names

If the stealer never runs, the second workflow takes over. It's the same logic as the original, with added credential formats and a fallback that uploads findings to a secret GitHub gist if the POST fails.

It hides behind eight filenames:

  • lint.yml
  • ci-check.yml
  • build-check.yml
  • repo-health.yml
  • audit.yml
  • code-review.yml
  • test-suite.yml
  • cache-cleanup.yml

Every one of those reads like routine repository hygiene. That's the design.

Who's Actually Affected, and Why So Many Are Abandoned

The 94 repositories with the stage 2 component belong to 66 owners.

Fifteen of those owners have since deleted, renamed, or made their repositories private. Of the ones still accessible, most look largely abandoned.

One had gone untouched for more than nine years. Sixty-five of the 79 accessible repositories hadn't seen a commit in over a year.

That profile explains why this works. Dormant repositories still hold live credentials. Nobody's reviewing them, nobody's watching their workflow files, and a new commit on a dead project raises no eyebrows.

The Timing Says Everything About the Adaptation

Read this sequence carefully, because it's the clearest evidence of an operator responding to a control.

GitHub started preventing the malicious workflow from running on October 9.

The waves on October 10 and 12 extracted no secrets.

The second-stage launcher arrived six days after the block, and it doesn't need a runner at all.

OpenSourceMalware was appropriately careful about causation. They can't say the block caused the shift. They can say the operator moved to a place GitHub's controls don't reach.

That distinction is worth keeping, and the practical conclusion holds either way. A platform-level block pushed activity into the developer's own tooling, where no platform control applies.

How to Check If You're Affected

Start with the token. If you've authorized an unexpected OAuth application recently, revoke it before doing anything else.

Then check your repositories for the stage 2 artifacts:

  • A .vscode/tasks.json containing a runOn: folderOpen trigger
  • A public/fonts/fa-solid-400.woff2 file
  • Any of the eight fallback workflow filenames in .github/workflows/
  • A cron entry running every five minutes that you didn't create

Then check network logs for 193.32.204[.]199, traffic to port 8443, and outbound HTTP on port 8080.

How to Respond If You Find It

The mitigation list from OpenSourceMalware is specific, and the first step isn't the one people expect.

  • Revoke GitHub access and tokens from a clean device. Not from the suspect machine. From a device you trust.
  • Change your passwords. And re-enroll 2FA where the code may have been captured.
  • Audit your repositories. Every one. Check forks and archived projects, not just active work.
  • Rotate every credential the workflows could reach. AWS, npm, PyPI, Kubernetes, SSH keys, AI provider keys. Assume all of them are gone.
  • Alert users who cloned the repositories. A fork carries the files. Anyone who cloned and opened the folder in VS Code may have run the task.
  • Remove the cron job before you assume the machine is clean. Five-minute execution means re-infection is a schedule, not an accident.

The Operator's Mistakes, and Why They Still Matter

Jenn Gile, co-founder of OpenSourceMalware, told The Hacker News that attribution isn't established, but the work suggests limited experience.

The stealer's environment-variable filter misses most API keys. Its home-directory search can time out and discard its own results. And a single IP handles exfiltration, phishing, and noisy internet scanning.

That last one is a gift. It's why searching for the IP still turns up infections in the wild.

A less competent operator running a campaign this effective is a specific kind of bad news. The tradecraft will get better.

FAQ

What is the GhostAction phishing campaign?

It's the second-stage evolution of the GhostAction supply chain attack. Instead of only injecting malicious GitHub Actions workflows, the actor now phishes open-source developers directly, steals OAuth tokens, and plants a stealer that persists on developer machines after the workflow is removed.

How does the phishing email work?

It impersonates as a GitHub security alert that appears to come from noreply@my-github[.]com about some unusual login activity originating from Moscow. It uses a reverse proxy server that forwards users to GitHub’s legitimate login page where the password and 2FA code get collected and an OAuth app is authenticated.

What privileges can the stolen OAuth token provide?

The device flow has requested the following scopes: repo, workflow, and read:org. The privileges include accessing the repositories, changing Actions workflows, and checking the membership in organizations.

What makes the font file dangerous?

This is because there is a credential stealer in the public/fonts/fa-solid-400.woff2 file which is automatically executed when a .vscode/tasks.json file is run with runOn: folderOpen.

Does removing the malicious workflow fix the compromise?

No. Stage 2 was built specifically to persist after workflow removal. It installs a cron job that runs attacker code every five minutes, and a second workflow activates if the stealer never executes.

What should I do first if I think I'm affected?

Revoking access to GitHub, changing my password and 2FA from a different machine is the first step. Then proceed to audit the repositories, rotate all accessible credentials, and notify everyone who cloned the affected repositories.

Source: The Hacker News
Professional Services

Explore Our Cybersecurity Services

Our insights are backed by hands-on service delivery. If your business needs professional cybersecurity support, our UK-based specialists are ready to help.

© 2016 – 2026 Red Secure Tech Ltd. Registered in England and Wales — Company No: 15581067