Threat reportSupply ChainTL-2026-2605
GHAPPIER Loader: npm Trusted-Publishing Abuse Compromises @dforge-core/dforge-mcp
An attacker compromised the maintainer account of the npm package @dforge-core/dforge-mcp and abused GitHub Actions OIDC trusted publishing and Sigstore attestation to ship trojanized versions 0.2.20 and 0.2.21, delivering a previously unreported four-stage, self-deleting loader (GHAPPIER) that opens a remote shell when the MCP server launches.
Key facts for GHAPPIER trusted-publishing npm
| Identifiers | TL-2026-2605 |
|---|---|
| Also known as | GHAPPIER, dforge-mcp npm Trusted-Publishing Compromise, PolinRider-linked npm Loader Incident |
| Severity | HIGH |
| First disclosed | |
| Exploitation status | Exploited in the wild |
| Affected products | dforge-core (npm) @dforge-core/dforge-mcp; common-stack (npm) @common-stack/generate-plugin |
| Weaknesses (CWE) | CWE-506, CWE-829 |
| Attribution | PolinRider (low confidence) |
| Nation-state nexus | North Korea |
| Malware | BeaverTail - S1246, DEV#POPPER, InvisibleFerret - S1245, OmniStealer, core |
| Tools | 0x3f0e5781d0855fb460661ac63257376db1941b2bb522499e4757ecb3ebd5dce3, 0xbe037400670fbf1c32364f762975908dc43eeb38759263e7dfcdabc76380811e, TMfKQEd7TJJa5xNZJZ2Lep838vrzrs7mAP, TXfxHUet9pJVU1BgVkBAbrES4YUc1nGzcG |
| MITRE ATT&CK techniques | T1027 Obfuscated Files or Information, T1059.007 JavaScript, T1070.004 File Deletion, T1071.001 Web Protocols, T1078 Valid Accounts, T1102.001 Dead Drop Resolver, T1195.002 Compromise Software Supply Chain, T1552.001 Credentials In Files, T1555.003 Credentials from Web Browsers, T1608.001 Upload Malware |
| Motivation | FINANCIAL |
| Target sectors | software development, technology, cryptocurrency, open source supply chain |
| Target regions | Global |
| Detection rules | 9 (1 shown free) |
| Sources | 8 cited sources |
Detection coverage for GHAPPIER trusted-publishing npm
As of 2026-09-21, Threadlinqs Intelligence publishes 9 detection rule(s) for TL-2026-2605 across Splunk SPL, Microsoft KQL and Sigma, covering 21 indicator(s) of compromise. The whole corpus is readable without an account. Every threat comes with one complete detection in Splunk SPL, Microsoft KQL and Sigma, free for everyone; a free account adds daily TL Creds to unlock the rest. Paid plans add a larger daily allowance, correlation and the MCP server. Threadlinqs MCP server · View plans.
Free detection: GHAPPIER Self-Deleting Implant & Browser Credential-Store Access (SPL)
Detects the GHAPPIER post-exploitation pattern: a Node.js MCP-server process spawning a command shell (the remote-shell stage), a file under node_modules/@dforge-core/dforge-mcp being created then deleted within a short window (the self-deleting implant behavior CloudSEK documents), and/or a node-spawned process reading browser credential-store files. Primary detection surface is the SPL flavor of a shared post-exploitation rule.
- Languages: Splunk SPL, Microsoft KQL, Sigma
- ATT&CK: T1059.007, T1027, T1070.004, T1555.003, T1552.001
- Data sources: sysmon_process_creation, sysmon_file_events, defender_for_endpoint_devicefileevents, defender_for_endpoint_deviceprocessevents
- Severity: critical
- Confidence: medium
Splunk SPL
index=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational"
(EventCode=1 ParentImage="*\\node.exe" (Image="*\\cmd.exe" OR Image="*\\powershell.exe" OR Image="*\\bash" OR Image="*\\sh"))
OR (EventCode=11 TargetFilename="*\\node_modules\\@dforge-core\\dforge-mcp\\*")
OR (EventCode=23 TargetFilename="*\\node_modules\\@dforge-core\\dforge-mcp\\*")
| eval action=case(EventCode=1,"shell_spawn",EventCode=11,"file_create",EventCode=23,"file_delete")
| transaction host TargetFilename startswith=eval(action="file_create") endswith=eval(action="file_delete") maxspan=5m
| where duration<300
| append
[ search index=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=11 ParentImage="*\\node.exe"
TargetFilename IN ("*\\Login Data","*\\Cookies","*\\Web Data")
| table _time host User Image TargetFilename ]
| table _time host User Image TargetFilename action duration
| sort - _timeMicrosoft KQL
let Creates = DeviceFileEvents
| where Timestamp > ago(24h)
| where ActionType == "FileCreated"
| where FolderPath has @"node_modules\@dforge-core\dforge-mcp";
let Deletes = DeviceFileEvents
| where Timestamp > ago(24h)
| where ActionType == "FileDeleted"
| where FolderPath has @"node_modules\@dforge-core\dforge-mcp";
Creates
| join kind=inner Deletes on DeviceId, FileName
| where (Timestamp1 - Timestamp) between (0s .. 5m)
| project DeviceId, FileName, Created = Timestamp, Deleted = Timestamp1, FolderPath
| union (
DeviceProcessEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName in~ ("node.exe","node")
| where FileName in~ ("cmd.exe","powershell.exe","bash","sh")
| project DeviceId, FileName, Created = Timestamp, Deleted = Timestamp, FolderPath = ProcessCommandLine
)
| union (
DeviceFileEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName in~ ("node.exe","node")
| where FileName in~ ("Login Data","Cookies","Web Data")
| project DeviceId, FileName, Created = Timestamp, Deleted = Timestamp, FolderPath
)
| sort by Created descSigma
title: Node.js Process Spawning Command Shell (Possible GHAPPIER Remote Shell)
id: b2f5d3c1-8e4f-4a2b-9d3b-7e9f2c4f6b12
status: experimental
description: Detects a Node.js process (MCP server runtime) spawning a command interpreter as a child process, consistent with the GHAPPIER loader opening a remote shell when the compromised dforge-mcp server launches, and the loader's self-deleting on-disk artifact and browser credential-store access.
references:
- https://www.cloudsek.com/blog/ghappier-malware-loader-npm-supply-chain-attack
author: AII-Detector — ThreadLinqs Intelligence
date: 2026-09-21
tags:
- attack.execution
- attack.t1059.007
- attack.defense-evasion
- attack.t1070.004
- attack.credential-access
- attack.t1555.003
- attack.t1552.001
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\node.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\bash.exe'
- '\wsl.exe'
filter_known_scripts:
CommandLine|contains:
- 'npm run'
- 'npm-cli.js'
condition: selection_parent and selection_child and not filter_known_scripts
falsepositives:
- Legitimate npm scripts that shell out via child_process for build tasks (node-gyp, electron-builder)
- Developer tooling that intentionally spawns a shell from a Node-based CLI
level: highKnown false positives:
- npm/build tooling that legitimately spawns a shell for postinstall or build scripts (node-gyp, electron-builder)
- Package managers/installers that create then quickly remove temporary files under node_modules during normal install/uninstall/prune operations
- EDR/AV or backup agents reading browser credential-store files for legitimate scanning purposes
Tuning notes: Baseline your org's legitimate node-spawns-shell build tooling (list known postinstall scripts) and add them to the filter; narrow the create/delete correlation window and require it originate specifically from a node_modules/@dforge-core path to cut noise from routine package churn. Treat a hit that combines the self-delete pattern with credential-store file access in the same session as high-confidence and auto-escalate to isolate the host per the researched immediate-remediation guidance (rotate all reachable credentials, since the implant may remain live after deleting its artifact).
- npm Trusted-Publishing Workflow Tampering & Trojanized dforge-mcp Install (KQL) · unlock with TL Creds
- GHAPPIER Self-Deleting Implant & Browser Credential-Store Access (KQL) · unlock with TL Creds
- Blockchain Dead-Drop C2 Resolution & PolinRider/DPRK-Linked Infrastructure Beaconing (KQL) · unlock with TL Creds
- npm Trusted-Publishing Workflow Tampering & Trojanized dforge-mcp Install (Sigma) · unlock with TL Creds
- GHAPPIER Self-Deleting Implant & Browser Credential-Store Access (Sigma) · unlock with TL Creds
- Blockchain Dead-Drop C2 Resolution & PolinRider/DPRK-Linked Infrastructure Beaconing (Sigma) · unlock with TL Creds
- npm Trusted-Publishing Workflow Tampering & Trojanized dforge-mcp Install (SPL) · unlock with TL Creds
- Blockchain Dead-Drop C2 Resolution & PolinRider/DPRK-Linked Infrastructure Beaconing (SPL) · unlock with TL Creds
Indicators of compromise for GHAPPIER trusted-publishing npm
As of 2026-09-21, Threadlinqs Intelligence tracks 21 indicators of compromise for TL-2026-2605.
How GHAPPIER trusted-publishing npm works
CloudSEK links a second payload family to the DPRK-linked PolinRider campaign, active since March 2026 across 65+ repositories, 73 files, and 22 accounts.
On September 9, 2026, an unidentified attacker gained control of the maintainer account for the npm package @dforge-core/dforge-mcp — a legitimate Model Context Protocol (MCP) server exposing 34 tools that let AI coding agents (Claude Code, Cursor, Zed) scaffold, build, and ship dForge modules — for a window of 105 minutes. CloudSEK's investigation found the attacker already held push access to the package's main branch at the start of the intrusion and assesses the likely root cause as a developer workstation infected by a malicious extension or package, though the initial infection vector itself remains unconfirmed. Using that access, the attacker modified the GitHub Actions release workflow (a three-line change to trigger release on any push to main), then 14 minutes later rewrote the workflow again to make publishing fully unattended. Two trojanized releases followed: version 0.2.20, which failed to become the active release, and version 0.2.21, which published successfully with valid npm provenance and a Sigstore attestation and remained the registry's 'latest' tag for 35 minutes 38 seconds before the legitimate maintainer reverted the account and shipped a clean 0.2.22.
Version 0.2.21 carried a single-line loader — dubbed GHAPPIER by CloudSEK — embedded at line 3,320 of an otherwise-unremarkable 99KB file. The loader does not execute on install; it fires only when the MCP server process is actually launched, meaning any environment that merely installed 0.2.21 as a dependency without starting the server was never compromised. Once triggered, GHAPPIER runs as a four-stage progression culminating in a general-purpose remote shell; the final-stage implant then deletes itself from disk the moment it finishes executing, removing the on-disk artifact while, per CloudSEK, remaining live and responsive for at least five days after the compromised account was recovered. CloudSEK frames the incident as an authorization-chain failure rather than a code-signing break: 'every action in this report is an authorised action taken with a stolen key' — npm/Sigstore provenance attests where an artifact was built, not whether the commit that triggered the build was honest, and the fraudulent attestation remains permanently visible in Sigstore's public transparency log, which is how the attacker's commit was ultimately identified.
CloudSEK ties a second payload family observed in the same investigation to PolinRider, a DPRK-linked loader/credential-harvesting campaign that OpenSourceMalware has tracked since March 2026 and which by CloudSEK's count now spans 65+ public repositories, 73 infected files, and 22 compromised accounts. CloudSEK reports that payload family reads its runtime configuration from empty Ethereum transactions (roughly $0.20 each), a blockchain dead-drop-resolver pattern consistent with PolinRider's documented use of TRON, Aptos, and BNB Smart Chain RPC endpoints elsewhere in the campaign. Attribution is contested: The Hacker News, Rescana, and OpenSourceMalware attribute the broader PolinRider/'Contagious Interview' cluster to North Korea's Lazarus Group and APT37 (ScarCruft) with high confidence, but CloudSEK states its own independent verification of the North Korea linkage for this specific incident was inconclusive — this research therefore records the actor as the PolinRider cluster with LOW attribution confidence pending corroboration. The campaign's other confirmed activity — a June 2026 Sonatype-tracked compromise of the unrelated npm package @common-stack/generate-plugin across 13 versions using the same 'rmcej%otb%' obfuscation decoder seed, widespread injection of obfuscated JavaScript into config files such as postcss.config.mjs, tailwind.config.js, and eslint.config.mjs, VS Code .vscode/tasks.json runOn:'folderOpen' auto-execution abuse, and anti-dated git-history rewriting — illustrates PolinRider's broader tradecraft, though none of those specific artifacts were confirmed present in the dforge-mcp compromise itself.
CloudSEK found no evidence of successful downstream organizational compromise and no exploitation of GitHub- or npm-platform vulnerabilities — the entire chain rode on a single stolen credential plus the trust normally placed in provenance attestation. As of the report date, no CVE had been assigned and no advisory existed in OSV or the GitHub Advisory Database for @dforge-core/dforge-mcp. Because this is the first confirmed abuse of npm trusted-publishing/OIDC to smuggle a working remote-access implant into an AI-agent tool's supply chain, and because the affected package sits directly in the MCP tool-installation path for Claude Code, Cursor, and Zed users, the incident is significant beyond its narrow, contained blast radius. CloudSEK's recommended defense — treating unexpected release-workflow modifications and multi-stage artifact patterns as the detection signal, rather than attempting to signature the self-deleting implant itself — reflects that provenance attestation alone is an insufficient trust boundary.
MITRE ATT&CK techniques by tactic:
Defense Evasion
T1027 Obfuscated Files or Information; T1070.004 File Deletion
Execution
Command and Control
T1071.001 Web Protocols; T1102.001 Dead Drop Resolver
Initial Access
T1078 Valid Accounts; T1195.002 Compromise Software Supply Chain
Credential Access
T1552.001 Credentials In Files; T1555.003 Credentials from Web Browsers
Resource Development
Remediation for GHAPPIER trusted-publishing npm
Patches
- Upgrade @dforge-core/dforge-mcp to 0.2.22 or later.
Immediate actions
- If any environment installed @dforge-core/dforge-mcp 0.2.20 or 0.2.21 AND launched the MCP server, treat the host as compromised: isolate it and rotate all credentials/API keys reachable from that host, since the implant deletes its on-disk artifact but may remain active.
- Pin @dforge-core/dforge-mcp to 0.2.22 or later and audit lockfiles across all repositories for any reference to 0.2.20 or 0.2.21.
- Audit GitHub Actions release-workflow history for packages you maintain for unauthorized edits that convert manual/reviewed publishing into unattended publishing triggered on push.
- Review the npm/Sigstore public transparency log for unexpected provenance attestations tied to your maintainers' commits.
Workarounds
- Do not launch the MCP server from any install that resolved 0.2.20 or 0.2.21 until node_modules is wiped and reinstalled against a pinned safe version.
Longer-term hardening
- Require branch protection and mandatory review on workflow files (.github/workflows/*) that can trigger a trusted-publishing release, not just on application source code.
- Do not treat npm/Sigstore provenance attestation alone as a trust signal — it attests where an artifact was built, not whether the triggering commit was honest; pair it with commit-review and anomaly-monitoring policy.
- Monitor for outbound connections from developer and CI environments to blockchain RPC endpoints (Ethereum, TRON, Aptos, BNB Smart Chain), a documented PolinRider dead-drop C2 pattern.
- Adopt supply-chain scanning (Socket.dev, OSV-Scanner, npm audit signatures) against the growing PolinRider package/version indicator set and monitor for multi-stage artifact patterns rather than relying on implant signatures alone.
Affected and fixed versions
- dforge-core (npm) — @dforge-core/dforge-mcp
Vulnerable versions: 0.2.20; 0.2.21
Fixed in: 0.2.22 - common-stack (npm) — @common-stack/generate-plugin
Vulnerable versions: 9.0.2-alpha.21; 9.0.2-alpha.22; and 11 further compromised 9.0.2-alpha.x releases per Sonatype-2026-003277
Fixed in: version preceding the compromised 9.0.2-alpha.x series
Timeline of GHAPPIER trusted-publishing npm
- OpenSourceMalware first publicly discloses tracking of the PolinRider campaign, reporting roughly 675 compromised GitHub repositories tied to obfuscated JavaScript loaders and anti-dated commits.
- PolinRider activity expands to 1,951 compromised public GitHub repositories across 1,047 unique owners, including a merge broadening the VS Code auto-run-task ('TasksJacker') abuse technique.
- Sonatype (advisory Sonatype-2026-003277) reports a separate PolinRider-linked npm compromise of @common-stack/generate-plugin across 13 malicious 9.0.2-alpha.x releases, using the same 'rmcej%otb%' obfuscation decoder seed and blockchain-sourced (Tron) payload retrieval later linked to this campaign family.
- GitHub account 'locked-ioc-21' is taken over and used to push bulk malicious commits across Go modules as part of PolinRider's cross-ecosystem expansion.
- The Hacker News reports North Korea-linked actors have published 108 malicious packages/extensions (162 malicious release artifacts) across npm, Packagist, Go modules, and the Chrome Web Store as part of the PolinRider/'Contagious Interview' cluster.
- Trojanized version 0.2.21 is published with valid npm provenance and Sigstore attestation and remains the latest release for 35 minutes 38 seconds before the legitimate maintainer reverts the account and publishes clean version 0.2.22.
- Trojanized version 0.2.20 of @dforge-core/dforge-mcp is published via the abused GitHub Actions OIDC trusted-publishing pipeline but fails to become the active/latest release.
- An attacker gains access to the @dforge-core/dforge-mcp npm maintainer's GitHub account for 105 minutes and modifies the release workflow to trigger unattended publishing on pushes to main.
- CloudSEK publishes its GHAPPIER analysis, identifying the four-stage self-deleting loader across 65+ repositories, 73 infected files, and 22 accounts, and linking a second payload family to the DPRK-linked PolinRider campaign.
- Infosecurity Magazine publishes coverage of the CloudSEK findings, bringing the dforge-mcp trusted-publishing abuse incident to wider public attention.
Sources cited for GHAPPIER trusted-publishing npm
- Active Exploitation Alert: North Korean PolinRider Supply Chain Attack Targets npm, Packagist, Go Modules, and Chrome Extensions rescana.com · Advisory · 2026-07-15
- GHAPPIER - One loader, sixty-five repositories, twenty-two accounts: an unreported loader family beside DPRK's PolinRider campaign cloudsek.com · Research · 2026-09-20
- PolinRider Jumps the Fence to Go, Packagist, npm, PyPI opensourcemalware.com · Research · 2026-07-01
- Hijacked npm Package Nearly Delivers PolinRider RAT sonatype.com · Vendor report · 2026-06-04
- Multiple trusted publishing configurations for npm github.blog · Vendor report · 2026-09-03
- dforge-mcp (GitHub repository) GitHub · Code repository · 2026-09-21
- Attackers Abuse npm Trusted Publishing to Compromise dforge-mcp Package infosecurity-magazine.com · News · 2026-09-21
- North Korean Hackers Publish 108 Malicious Packages and Extensions in PolinRider Campaign The Hacker News · News · 2026-07-01
More from PolinRider
- ChainDrop npm Worm and PolinRider DPRK-Linked Operation Use Blockchain C2 to Steal Cloud and CI/CD Credentials
- Joyfill npm Packages Compromised with Blockchain C2 Loader
- ChainVeil and ViteVenom Malware Linked to DPRK PolinRider Supply-Chain Campaign
- PolinRider DPRK Supply-Chain Campaign: Confirmed GitHub Footprint Grows 6.5x Since March (JADESNOW/Beavertail/InvisibleFerret Loaders, DEV#POPPER & OmniStealer Payloads)
- PolinRider: DPRK Supply-Chain Campaign Hides BeaverTail/InvisibleFerret Malware in JS Build Config Files (tailwind.config.js et al.)
- astro.config.mjs Supply Chain Attack via Blockchain Dead-Drop C2 (PolinRider / js.jadesnow)
Previous and next in supply chain
Further reading
Drafted by the Threadlinqs research pipeline from 8 sources; published ; last updated —.