# 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. CloudSEK links a second payload family to the DPRK-linked PolinRider campaign, active since March 2026 across 65+ repositories, 73 files, and 22 accounts.

- **Published:** 2026-09-21T00:00:00Z
- **Last reviewed:** 2026-09-21T00:00:00Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-2605
- **ID:** TL-2026-2605
- **Severity:** HIGH
- **Category:** SUPPLY_CHAIN
- **Status:** ACTIVE
- **Actor:** PolinRider (North Korea)
- **Detections:** 9 · **IOCs:** 21 (full data via the Threadlinqs MCP server — Purple tier)

## Description

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

- T1195.002 Compromise Software Supply Chain
- T1078 Valid Accounts
- T1059.007 JavaScript
- T1027 Obfuscated Files or Information
- T1070.004 File Deletion
- T1071.001 Web Protocols
- T1102.001 Dead Drop Resolver
- T1555.003 Credentials from Web Browsers
- T1552.001 Credentials In Files
- T1608.001 Upload Malware

## Sources

- [Attackers Abuse npm Trusted Publishing to Compromise dforge-mcp Package](https://www.infosecurity-magazine.com/news/attackers-abuse-npm-trusted/)
- [GHAPPIER - One loader, sixty-five repositories, twenty-two accounts: an unreported loader family beside DPRK's PolinRider campaign](https://www.cloudsek.com/blog/ghappier-malware-loader-npm-supply-chain-attack)
- [North Korean Hackers Publish 108 Malicious Packages and Extensions in PolinRider Campaign](https://thehackernews.com/2026/07/north-korean-hackers-publish-108.html)
- [PolinRider Jumps the Fence to Go, Packagist, npm, PyPI](https://opensourcemalware.com/blog/polinrider-jumps-the-fence)
- [Hijacked npm Package Nearly Delivers PolinRider RAT](https://www.sonatype.com/blog/hijacked-npm-package-attempts-to-deliver-polinrider-linked-rat)
- [Active Exploitation Alert: North Korean PolinRider Supply Chain Attack Targets npm, Packagist, Go Modules, and Chrome Extensions](https://www.rescana.com/post/active-exploitation-alert-north-korean-polinrider-supply-chain-attack-targets-npm-packagist-go-modules-and-chrome-extens)
- [dforge-mcp (GitHub repository)](https://github.com/dforge-core/dforge-mcp)
- [Multiple trusted publishing configurations for npm](https://github.blog/changelog/2026-09-03-multiple-trusted-publishing-configurations-for-npm/)

## Full data

Detection queries (Splunk SPL / Microsoft KQL / Sigma) and IOC values require the Threadlinqs MCP server (Purple tier): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-2605
