PolinRider — North Korea-Linked Open-Source Supply-Chain Campaign

108 packages / 162 malicious release artifacts across npm, Packagist, Go modules and Chrome — JS loaders hidden in config files and VS Code folderOpen tasks pull an XOR-encrypted stage-2 from blockchain RPC dead-drops, delivering the DEV#POPPER RAT and OmniStealer.
Threat
PolinRider (Famous Chollima / Contagious Interview cluster)
Severity
ACTIVE DPRK SUPPLY-CHAIN
Type
Software Supply Chain · Developer Workstation
Access Vector
Malicious package install / repo clone → auto-run
Version
v0.2 · 2026-07-09
Author
HuntPack
Confidence
High (primary Socket research; multi-outlet corroboration)
01

Executive Summary

PolinRider is a North Korea-linked open-source supply-chain campaign tracked by the Socket Threat Research Team and attributed to the broader Famous Chollima / Contagious Interview activity cluster. Active since December 2025 and still expanding into July 2026, Socket identified 162 malicious release artifacts across 108 packages and extensions spanning four ecosystems — npm, Packagist (PHP), Go modules (80 compromised modules), and one Chrome extension. The operators compromise legitimate maintainer accounts and GitHub repositories, then plant obfuscated JavaScript loaders and publish trojanized versions downstream.

The signature tradecraft is developer-workstation auto-execution. Malicious JS is appended into legitimate config files — vite.config.js, eslint.config.js, and other *config.js — so it runs the next time the project builds or lints, and injected .vscode/tasks.json entries with "runOn": "folderOpen" auto-execute a fake .woff2 font file via Node.js the moment a developer opens the folder in VS Code. The loader reaches out to public blockchain RPC infrastructure (TRON, Aptos, BNB Smart Chain) used as a dead-drop resolver, retrieves an encrypted second stage, XOR-decrypts it with embedded keys and executes it with eval(). Follow-on payloads are the DEV#POPPER RAT (command execution, socket.io-based C2) and the OmniStealer information stealer (credential, browser-data and crypto-wallet theft). Operators cover their tracks by rewriting git history via force pushes with anti-dated commits and hiding code with whitespace padding beyond the default screen width.

This is a distinct named campaign from the previously-packaged BeaverTail / Contagious Interview interview-lure activity, though it shares the same DPRK cluster and the "developer as patient-zero" objective. The blast radius is any developer endpoint, CI runner, or container build host that installed an affected package, cloned a poisoned repo, or opened one in VS Code.

Defender priority: The highest-fidelity catch is a node process executing a .woff2 font file, or VS Code (Code/code) spawning node/a shell on folder open. Pair with a dev-context process resolving a blockchain RPC domain and a secondary detached interpreter. Sweep every dev repo and build agent for the PolinRider artifacts (.vscode/tasks.json folderOpen tasks, fake .woff2 files, and the named repos/packages in §4/§10), and treat any hit as a credential-compromise event — rotate developer secrets and wallets.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Fwd
1Socket Threat ResearchPrimary attribution + technical anatomy: 162 artifacts / 108 packages across npm/Packagist/Go/Chrome; config-file + .vscode/tasks.json folderOpen loaders; fake .woff2 execution; blockchain-RPC dead-drops (TRON/Aptos/BNB); XOR+eval(); DEV#POPPER + OmniStealer; compromised accounts/reposyes
1The Hacker News"North Korean Hackers Publish 108 Malicious Packages and Extensions in PolinRider Campaign" — campaign naming, counts, ecosystem scopeyes
2SecurityWeekCorroborates NK developer-targeting supply-chain framing and payload rolespartial
2Developer-Tech / DevOps.comPackagist-ecosystem expansion; maintainer-account-takeover mechanicspartial
2RescanaActive-exploitation framing across npm/Packagist/Go/Chromepartial

Hunter note: The abused blockchain RPC endpoints are legitimate public infrastructure (TRON/Aptos/BNB) used as dead-drop resolvers — they are NOT attacker-owned IOCs, so Q3 is a behavioral hunt with expected FPs on genuine web3 developers, not a blocklist. Atomic file hashes and stage-2 C2 IPs for DEV#POPPER / OmniStealer were not broadly published at pack time — behavioral detection is primary; the IOC CSV uses REPLACE_WITH placeholders for hashes so no fabricated indicator ships. The named repos/accounts (Xpos587, 7span/sevenspan) are sample compromised identities from Socket, not an exhaustive list — pivot on Socket's live tracker for the full package inventory.

03

Hunt Brief & Attack Chain

Hunt Hypotheses (fidelity-ordered)

  1. H1 (high): A node process executes a .woff2 file — a font asset should never be a Node.js entry point. low FP
  2. H2 (medium): VS Code (Code/code) spawns node or a shell immediately on folder open — the runOn:folderOpen task trigger. med FP
  3. H3 (medium): A dev-tool-context process (node/Code/npm) resolves a public blockchain RPC domain (TRON/Aptos/BNB) — loader dead-drop fetch. med/high FP
  4. H4 (medium): A node process spawns a secondary interpreter/shell child — decoded stage-2 execution after eval(). med FP
  5. H5 (medium): A node/editor-context process makes a long-lived outbound connection to a non-RFC1918 raw IP — socket.io DEV#POPPER C2 / OmniStealer exfil. med FP
  6. H6 (medium): PolinRider artifacts on disk — a .vscode/tasks.json with a folderOpen task, a fake .woff2, or the named compromised packages/repos. med FP
  7. H7 (medium): A dev-context process reads a browser credential store or crypto-wallet directory — OmniStealer collection. med FP

Attack Chain

StepBehaviorTelemetryHunt Angle
1 · Resource DevelopmentCompromise maintainer accounts/repos; publish 108 trojanized packages across npm/Packagist/Go/Chromen/a (registry-side)SCA / repo audit (H6)
2 · Initial AccessDeveloper installs package / clones poisoned repo / opens it in VS CodeFileWrite / ProcessRollup2Artifact sweep (H6)
3 · ExecutionLoader in *config.js runs on build/lint; .vscode/tasks.json folderOpen runs fake .woff2 via nodeProcessRollup2node runs .woff2 (H1); Code spawns node (H2)
4 · C2 / IngressLoader fetches encrypted stage-2 from blockchain RPC dead-drop (TRON/Aptos/BNB)DnsRequest / NetworkConnectIP4RPC dead-drop (H3)
5 · Defense EvasionXOR-decrypt embedded payload; eval() execute; whitespace-padding obfuscationProcessRollup2node spawns child interpreter (H4)
6 · Command & ControlDEV#POPPER RAT: command exec over socket.io C2NetworkConnectIP4node raw-IP egress (H5)
7 · Collection / Credential AccessOmniStealer harvests credentials, browser data, crypto walletsFileWrite / file accessWallet/browser store read (H7)
8 · Defense EvasionGit history rewrite via force push with anti-dated commitsn/a (VCS-side)Repo forensics / branch-protection audit
04

Consolidated IOC Table

TypeValueConfActionContext
GitHub accountXpos587highhuntCompromised/operator identity (Socket)
RepoXpos587/git2md, Xpos587/markfetchhighhuntPoisoned repos with injected JS loaders
RepoArtiffusion-Inc/mirofishhighhuntPoisoned repo (Socket)
Packagist / orgsevenspan namespace · GitHub org 7span · 7span/react-listhighhuntCompromised Packagist namespace / repo
File (loader host)vite.config.js, eslint.config.js, other *config.jsmediumhuntObfuscated JS appended into legit config
File (auto-run).vscode/tasks.json with "runOn": "folderOpen"highhuntAuto-executes payload on folder open
File (payload)Fake .woff2 font file executed via Node.jshighhuntFont asset masquerading as JS entry point
BehaviorLoader → public blockchain RPC dead-drop (TRON, Aptos, BNB Smart Chain)mediumhuntLegit RPC infra abused as C2 resolver
BehaviorXOR-decrypt embedded stage-2 + eval() executionmediumhuntSecond-stage deobfuscation/exec
MalwareDEV#POPPER (command exec, socket.io C2)highhuntStage-2 RAT
MalwareOmniStealer (credential/browser/wallet theft)highhuntStage-2 infostealer
SHA256REPLACE_WITH_SHA256 (loader / .woff2 / stage-2)mediumdetectPopulate when vendor hashes publish
IPv4 / domainREPLACE_WITH_STAGE2_C2mediumdetectDEV#POPPER socket.io C2 — not yet public
05

Affected Surface & Telemetry Matrix

SurfaceRequired TelemetryPriorityGap Risk
Developer endpoints (mac/Win/Linux)ProcessRollup2, DnsRequest, NetworkConnectIP4, file-write eventscriticalLow — Falcon sensor coverage typical
CI/CD build agentsProcessRollup2, NetworkConnectIP4criticalHigh — ephemeral runners may lack sensor
Container build hostsProcessRollup2 (container-aware), NetworkConnectIP4highHigh — short-lived containers
Network egressDnsRequest, NetworkConnectIP4, proxy logshighMedium — RPC dead-drop rides legit domains
Source repos / VCSBranch-protection + force-push audit, SCA/SBOMhighHigh — VCS-side, out-of-band tooling
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
Resource DevelopmentT1195.002 — Supply Chain Compromise: Software Supply Chain108 trojanized packages across npm/Packagist/Go/ChromeH6 / SCA + Q6
ExecutionT1059.007 — JavaScriptLoader in config files; fake .woff2 run via nodeQ1 / Q2
ExecutionT1204.002 — User Execution: Malicious FileDeveloper opens poisoned repo / installs packageQ2 / Q6
Command & ControlT1102 — Web Service (dead-drop resolver)Stage-2 fetched from public blockchain RPCQ3
Command & ControlT1105 — Ingress Tool TransferEncrypted second-stage downloadQ3 / Q5
Defense EvasionT1140 — Deobfuscate/Decode Files or InformationXOR-decrypt embedded payload; whitespace paddingQ4
Defense EvasionT1027 — Obfuscated Files or Informationeval() of decoded stage-2; hidden config JSQ1 / Q4
Command & ControlT1071.001 — Application Layer Protocol: WebDEV#POPPER socket.io C2Q5
Credential AccessT1555 — Credentials from Password StoresOmniStealer browser credential theftQ7
Collection / ImpactT1005 / T1657 — Data from Local System / crypto theftOmniStealer wallet + browser-data exfilQ7
07

Native Audit-Log Hunts (non-CQL)

Repo / project sweep for PolinRider auto-run artifacts (dev endpoints + agents)

# VS Code folderOpen auto-run tasks (the auto-execution trigger)
grep -rln "folderOpen" --include=tasks.json .
# Fake font files that are actually JS entry points
find . -type f -name "*.woff2" -exec sh -c 'head -c 4 "$1" | grep -qv "wOF2" && echo "SUSPECT: $1"' _ {} \;
# Malicious JS appended into config files (look for eval / long whitespace-padded lines)
grep -rEln "eval\(|Buffer\.from\(.*base64|fromCharCode" --include=*config.js .

Named compromised packages / repos (SCA + git remotes)

# npm / Packagist / Go dependency trees
npm ls 2>/dev/null | grep -Ei "git2md|markfetch|react-list"
grep -rEln "Xpos587/(git2md|markfetch)|Artiffusion-Inc/mirofish|7span/react-list|sevenspan/" .
# Go module cache
grep -rEln "git2md|markfetch|mirofish" "$(go env GOPATH 2>/dev/null)/pkg/mod" 2>/dev/null

Blockchain RPC dead-drop egress from dev tooling (proxy/DNS logs)

# In your web-proxy / DNS log platform, look for build/editor hosts resolving:
#   *.trongrid.io  *.aptoslabs.com  bsc-dataseed*  *.bnbchain.org
# from a node/Code/npm process context outside your known web3 dev group.

Note: Because operators rewrite git history with anti-dated force pushes, a clean-looking commit log does NOT clear a repo — audit branch-protection/force-push events server-side (GitHub audit log) and pivot on the endpoint process/network telemetry (Q1–Q5).

08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · node executing a .woff2 font file
CONF HIGHFP LOWCOST LOW

Looks for: a node process whose command line references a .woff2 file — the PolinRider folderOpen payload. A font asset should never be a Node.js entry point, making this near-zero-FP.

// HUNT: node executing a .woff2 font file (PolinRider loader)
// MITRE: T1059.007, T1027
// CONF: high  FP: low  COST: low | REQUIRES: ProcessRollup2
// FALSE POSITIVES: none expected — .woff2 is a font, not a JS entry point
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ImageFileName=/(\/|\\)node(\.exe)?$/i
| CommandLine=/\.woff2(\b|["' ])/i
| table([ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine, SHA256HashData, aid], limit=200)
Q2 · VS Code spawning node / shell on folder open
CONF HIGHFP MEDCOST LOW

Looks for: the VS Code editor (Code/code) spawning node or a shell — the runOn:folderOpen task trigger. FP: VS Code legitimately spawns node for extensions/build tasks; pair with Q1/Q3 on the same host and baseline expected task activity.

// HUNT: VS Code spawns node/shell on folder open (folderOpen task)
// MITRE: T1059.007, T1204.002
// CONF: high  FP: medium  COST: low | REQUIRES: ProcessRollup2
// FALSE POSITIVES: legit VS Code tasks / extensions spawning node
// TUNING: correlate with Q1 (.woff2) or Q3 (RPC) on the same aid; baseline dev hosts
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^Code( - Insiders)?(\.exe)?$/i
| ImageFileName=/(\/|\\)(node|sh|bash|zsh|cmd|powershell|osascript)(\.exe)?$/i
| table([ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine, aid], limit=200)
Q3 · dev tooling resolving blockchain RPC dead-drop
CONF MEDFP HIGHCOST LOW

Looks for: a node/editor/npm-context process resolving a public blockchain RPC domain (TRON/Aptos/BNB) — the loader dead-drop fetch. FP: genuine web3 developers query these endpoints constantly — this is a behavioral hunt, not a blocklist. Scope to hosts outside your known blockchain/web3 dev group and correlate with Q1/Q2.

// HUNT: dev-context process resolving blockchain RPC dead-drop
// MITRE: T1102, T1105
// CONF: medium  FP: high  COST: low | REQUIRES: DnsRequest
// FALSE POSITIVES: legitimate web3/crypto developers — RPC infra is public/legit
// TUNING: exclude known web3 dev hosts; require a node/Code/npm ContextBaseFileName
#event_simpleName=DnsRequest
| DomainName=/(trongrid\.io|aptoslabs\.com|bsc-dataseed|bnbchain\.org|binance\.org)$/i
| ContextBaseFileName=/^(node|Code|npm|npx|go)(\.exe)?$/i
| table([ComputerName, UserName, ContextBaseFileName, DomainName, aid], limit=200)
Q4 · node spawning a secondary interpreter (stage-2 exec)
CONF MEDFP MEDCOST MED

Looks for: a node process spawning a shell or a second interpreter — the decoded stage-2 executing after eval(). FP: build toolchains legitimately shell out; require a suspicious ancestor (editor/package manager) and correlate with Q1–Q3.

// HUNT: node spawns secondary shell/interpreter (decoded stage-2)
// MITRE: T1059.007, T1140
// CONF: medium  FP: medium  COST: medium | REQUIRES: ProcessRollup2
// FALSE POSITIVES: build tools legitimately shelling out (webpack/postinstall)
// TUNING: correlate on aid with Q1/Q2/Q3; scope to editor/package-manager ancestry
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^node(\.exe)?$/i
| ImageFileName=/(\/|\\)(sh|bash|zsh|cmd|powershell|osascript|curl|wget|node)(\.exe)?$/i
| table([ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine, aid], limit=200)
Q5 · node/editor outbound to raw external IP (socket.io C2)
CONF MEDFP MEDCOST MED

Looks for: a node-context process connecting outbound to a non-RFC1918 raw IP — generalizes the DEV#POPPER socket.io C2 / OmniStealer exfil beyond named IOCs. FP: node apps that talk to IP-based SaaS; exclude known egress ranges and focus on install/edit windows.

// HUNT: node/editor outbound to raw external IP (DEV#POPPER C2 / exfil)
// MITRE: T1071.001, T1105
// CONF: medium  FP: medium  COST: medium | REQUIRES: NetworkConnectIP4
// FALSE POSITIVES: node apps beaconing to legitimate IP-based services
// TUNING: exclude RFC1918 + known SaaS ranges; correlate with Q1-Q4 on aid
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^(node|Code)( - Insiders)?(\.exe)?$/i
| RemoteAddressIP4!=/^(10\.|127\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)/
| table([ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, aid], limit=200)
Q6 · PolinRider artifacts written to disk
CONF MEDFP MEDCOST MED

Looks for: file-system evidence of the loader dropzone — a .vscode/tasks.json or a .woff2 written into a project tree. FP: legitimate projects ship both; treat as a triage lead, then inspect the file for a folderOpen task / non-font content. ⚠ validate in tenant — non-PE file-write telemetry (FileCreateInfo) is not populated in every environment; confirm the event flows before relying on this query.

// HUNT: PolinRider on-disk artifacts (folderOpen task / fake .woff2)
// MITRE: T1195.002, T1204.002
// CONF: medium  FP: medium  COST: medium | REQUIRES: FileCreateInfo (validate in tenant)
// FALSE POSITIVES: legit .vscode/tasks.json and .woff2 assets — inspect content
// TUNING: prioritize tasks.json written by node/Code/git; pair with §7 grep sweep
#event_simpleName=/FileCreateInfo|NewScriptWritten/
| TargetFileName=/\.vscode[\/\\]tasks\.json$/i OR TargetFileName=/\.woff2$/i
| table([ComputerName, UserName, TargetFileName, ContextBaseFileName, aid], limit=200)
Q7 · OmniStealer reading browser / wallet stores
CONF MEDFP MEDCOST MED

Looks for: a node-context process touching a browser credential store or crypto-wallet directory — OmniStealer collection. FP: password managers / legit wallet apps access these paths; require a node/editor context, not the browser itself. ⚠ validate in tenant — file-read/write visibility for these paths varies; confirm telemetry.

// HUNT: node-context process reading browser/wallet credential stores
// MITRE: T1555, T1005
// CONF: medium  FP: medium  COST: medium | REQUIRES: FileCreateInfo/read (validate in tenant)
// FALSE POSITIVES: password managers / legit wallet apps — require node context
// TUNING: exclude the browser's own process; alert when ContextBaseFileName is node
#event_simpleName=/FileCreateInfo|NewScriptWritten/
| TargetFileName=/(Login Data|Local State|key3\.db|key4\.db|logins\.json)$/i OR TargetFileName=/[\/\\](Ethereum|Electrum|Exodus|Ledger Live|MetaMask)[\/\\]/i
| ContextBaseFileName=/^(node|Code)( - Insiders)?(\.exe)?$/i
| table([ComputerName, UserName, ContextBaseFileName, TargetFileName, aid], limit=200)
09

CrowdStrike Custom IOA Recommendations

IOA NamePatternActionExclusions
node-executes-woff2Process node, CommandLine contains .woff2Detect (Critical) → Prevent after tuningNone expected
vscode-folderopen-spawnParent Code/code spawning node/shellDetect (High)Baselined dev hosts with known build tasks
node-child-interpreterParent node spawning shell/second interpreterDetect (Medium)Known build toolchains that shell out
node-rpc-deaddropnode/Code resolving TRON/Aptos/BNB RPC domainDetect (Medium) — Investigate onlyKnown web3/crypto dev group

Promotion path: Q1 is a strong IOA/Prevent candidate (near-zero FP, attacker-specific). Q2 is a good Detect after per-environment baselining. Q3–Q7 remain Investigate-only hunt queries until FP-baselined, and are strongest when correlated on the same aid.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
sha256,REPLACE_WITH_LOADER_SHA256,prevent,critical,2027-01-09,PolinRider JS loader / fake .woff2,campaign:PolinRider
sha256,REPLACE_WITH_STAGE2_SHA256,prevent,critical,2027-01-09,DEV#POPPER / OmniStealer stage-2,campaign:PolinRider
domain,REPLACE_WITH_STAGE2_C2,detect,high,2026-10-09,DEV#POPPER socket.io C2 (populate when public),campaign:PolinRider
ipv4,REPLACE_WITH_STAGE2_C2_IP,detect,high,2026-10-09,stage-2 C2 IP (populate when public),campaign:PolinRider
Behavioral Signatureshunt logic
woff2_exec   := node <something>.woff2         (font run as JS entry point)
folderopen   := Code -> node|sh   .vscode/tasks.json "runOn":"folderOpen"
deaddrop     := node|Code -> DNS *.trongrid.io|*.aptoslabs.com|bsc-dataseed*
stage2_exec  := node -> sh|bash|node   (eval() of XOR-decoded payload)
c2           := node|Code -> outbound raw non-RFC1918 IP (socket.io)
collection   := node -> read Login Data|MetaMask|Exodus|Ledger Live
Named Packages / ReposSCA / repo blocklist
# Sample compromised repos/accounts (Socket — not exhaustive)
Xpos587/git2md
Xpos587/markfetch
Artiffusion-Inc/mirofish
7span/react-list       (GitHub org: 7span)
Packagist namespace:   sevenspan
Operator/compromised account: Xpos587
# Full live inventory: socket.dev/supply-chain-attacks/polinrider
Dev-Env Audit Checklistremediation
# 1. Sweep repos/agents for .vscode/tasks.json "folderOpen" tasks + fake .woff2
# 2. grep *config.js for appended eval()/base64 loaders (whitespace-padded)
# 3. Set VS Code: "security.workspace.trust.enabled": true (no auto task run)
# 4. Enforce --ignore-scripts in CI; disable auto-run tasks on untrusted repos
# 5. Rotate ALL developer secrets reachable from affected hosts:
#    git/npm tokens, cloud keys, SSH keys, browser-stored creds, crypto wallets
# 6. Reimage any endpoint that opened a poisoned repo / ran an affected install
# 7. Audit GitHub org for force-push / anti-dated-commit events; enforce protection
11

Detection Validation Gates

GateCheckPass Criteria
Telemetry readyProcessRollup2, DnsRequest, NetworkConnectIP4 flowing from dev + CI hosts; confirm file-write events for Q6/Q7Event types present in last 24h
Benign baselineRun Q2 & Q3 over 7d; catalog legit VS Code node-spawns and web3-dev RPC egressKnown-good list built; exclusions added
Positive testIn a sandbox, run node ./x.woff2 and a VS Code folderOpen task spawning nodeQ1 & Q2 fire on the test artifacts
CorrelationConfirm Q1–Q5 can be joined on aid to build a single-host chainMulti-signal host list produced
PromotionQ1 FP-clean for 7d; Q2 baselinedPromote Q1 to Custom IOA / Prevent
12

Hardening — Tiered

Immediate (this week)
  • Enable VS Code Workspace Trust: ensure security.workspace.trust.enabled = true (default) and never "Trust" an unknown repo — untrusted folders do not auto-run folderOpen tasks. (M1042 Disable or Remove Feature)
  • Disable install scripts in CI: npm ci --ignore-scripts / pnpm config set enable-pre-post-scripts false on build agents. (M1042)
  • Sweep + remove artifacts: delete PolinRider folderOpen tasks, fake .woff2 and named packages/repos (§7/§10). (M1051 Update Software)
  • Rotate developer secrets reachable from any affected host: git/npm tokens, cloud/SSH keys, browser creds, crypto wallets. (M1027 Password Policies / M1032 MFA)
Near term (1–4 weeks)
  • Egress control for dev/CI: allowlist package registries + known mirrors; alert on build/editor processes reaching blockchain RPC or raw external IPs. (M1037 Filter Network Traffic)
  • VCS branch protection: block force-push to protected branches, require signed commits + reviews, and monitor the GitHub org audit log for anti-dated-commit / history-rewrite events. (M1018 User Account Management)
  • SCA in CI: fail builds on newly-published / low-reputation dependency versions; quarantine packages under a cooldown. (M1016 Vulnerability Scanning)
Strategic (1–3 months)
  • Private registry / pull-through proxy (Verdaccio, Artifactory, GitHub Packages) with quarantine on newly-published versions. (M1016)
  • Ephemeral, network-segmented build runners with no standing credentials and per-job secret injection — contains developer-workstation compromise. (M1030 Network Segmentation)
  • Developer endpoint segmentation: isolate build/dev hosts from crown-jewel data; enforce hardware-backed keys so stolen browser/wallet material is non-replayable. (M1032 MFA / M1030)
13

Deployable Playbooks

1 · Enforce VS Code Workspace Trust + disable auto-tasks (settings.json)

// User settings.json — block untrusted-folder auto execution
{
  "security.workspace.trust.enabled": true,
  "security.workspace.trust.untrustedFiles": "prompt",
  "task.allowAutomaticTasks": "off"
}
// Fleet-manage via MDM / GPO by deploying this to each user's settings.json

2 · Sweep & flag PolinRider artifacts (bash)

#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-$HOME}"
echo "[*] folderOpen auto-run tasks:"
grep -rln "folderOpen" --include=tasks.json "$ROOT" || true
echo "[*] .woff2 files that are NOT real fonts (bad magic):"
find "$ROOT" -type f -name "*.woff2" -exec sh -c \
  'head -c 4 "$1" | grep -qv "wOF2" && echo "  SUSPECT: $1"' _ {} \; 2>/dev/null || true
echo "[*] eval/base64 loaders appended into config files:"
grep -rEln "eval\(|Buffer\.from\([^)]*base64|fromCharCode" --include=*config.js "$ROOT" || true
echo "[*] named compromised repos/packages:"
grep -rEln "Xpos587/(git2md|markfetch)|Artiffusion-Inc/mirofish|7span/react-list|sevenspan/" "$ROOT" || true

3 · CI hardening (pipeline YAML fragment)

# Never run lifecycle scripts on untrusted installs
npm ci --ignore-scripts
pnpm config set enable-pre-post-scripts false
yarn config set enableScripts false
# Deny build-agent egress except to the registry/mirror allowlist

4 · Falcon RTR triage one-liner (macOS/Linux dev host)

find "$HOME" -maxdepth 6 -path "*/.vscode/tasks.json" -exec grep -l "folderOpen" {} \; 2>/dev/null
# Windows (PowerShell)
Get-ChildItem $env:USERPROFILE -Recurse -Filter tasks.json -EA SilentlyContinue |
  Select-String -Pattern 'folderOpen' | Select-Object Path
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-contain (Falcon RTR contain) any host with Q1/Q2 hits or a confirmed folderOpen task; freeze affected CI runnersSOC / IRaid list, contain timestamps
EradicateKill stage-2 node/child process; delete fake .woff2, malicious tasks.json, and appended config-file JS; remove named packages/reposIR / DevOpsProcess tree, file list
EradicateRotate ALL secrets reachable from affected hosts (git/npm/cloud/SSH tokens, browser creds, crypto wallets) — assume OmniStealer exfilIR / PlatformRotation tickets, wallet moves
RecoverReimage endpoints that opened a poisoned repo / ran an affected install; restore from known-good imagesDevOps / ITReimage records
RecoverRe-enable dev/CI only after Workspace Trust + ignore-scripts + egress control enforced; monitor Q3/Q5 for re-beaconSOC7-day clean window
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1059.007 / T1027node executes .woff2Q1YesGood
T1204.002VS Code folderOpen spawnQ2YesGood
T1102 / T1105blockchain RPC dead-dropQ3NoPartial — high FP, hunt only
T1059.007 / T1140stage-2 interpreter spawnQ4YesPartial
T1071.001 / T1105socket.io C2 / exfilQ5NoPartial
T1195.002 / T1204.002on-disk loader artifactsQ6NoPartial — file-write telemetry varies
T1555 / T1005browser/wallet theftQ7NoPartial — file-access telemetry varies
Registry-side publishAccount/repo takeoverNoGAP — SCA/VCS audit only
Stage-2 atomic IOCsDEV#POPPER / OmniStealer hashes + C2NoGAP — not yet public

Behavioral coverage of the auto-run→fetch→exec chain is strong at the front (Q1/Q2) and correlative in the middle (Q3–Q5). Gaps are registry/VCS-side takeover (needs SCA/SBOM + GitHub audit) and stage-2 atomic indicators (await published hashes/C2 — see CSV placeholders). Validate §11 gates before promoting Q1 to Prevent.

16

Hunt Summary Ticket

TITLE:      PolinRider (DPRK supply-chain) — dev-workstation auto-run → RAT/stealer hunt
SEVERITY:   Critical (active DPRK supply-chain; 108 packages, 4 ecosystems)
SCOPE:      Developer endpoints, CI/CD build agents, container build hosts
HYPOTHESIS: A poisoned package/repo auto-runs a JS loader (config file /
            .vscode folderOpen / fake .woff2) that fetches an XOR+eval stage-2
            (DEV#POPPER + OmniStealer) via blockchain RPC dead-drops.
QUERIES:    Q1 node runs .woff2 | Q2 Code folderOpen spawn | Q3 RPC dead-drop
            Q4 node child interpreter | Q5 node raw-IP C2 | Q6 on-disk artifacts
            Q7 browser/wallet theft
DO FIRST:   1) Run Q1/Q2 fleet-wide; contain any hits
            2) Sweep repos/agents for folderOpen tasks + fake .woff2 (§7)
            3) Enforce VS Code Workspace Trust + --ignore-scripts in CI
FINDINGS:   <fill: hosts, aids, packages/repos, secrets/wallets exposed>
GAPS:       Registry/VCS-side takeover; stage-2 atomic hashes + C2 pending
ACTIONS:    Rotate developer secrets + wallets; reimage affected hosts;
            audit GitHub org for force-push / anti-dated commits
OWNER:      <assign SOC/IR lead>
VERSION:    v0.2 · 2026-07-09 · HuntPack
17

Changelog

v0.22026-07-24CQL correctness pass. All table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.
v0.12026-07-09Initial HuntPack. 7 CQL queries (Q1 IOA/Prevent candidate; Q2 Detect; Q3–Q7 Investigate/correlate), behavioral IOC appendix (no fabricated atomic indicators — hashes/C2 are placeholders pending vendor publication), tiered hardening, deployable playbooks, containment runbook. Built from primary Socket Threat Research reporting with The Hacker News / SecurityWeek corroboration.
18

References

TierSourceUsed ForAccess Date
1Socket — PolinRider: North Korea-Linked Supply Chain Campaign ExpandsPrimary attribution, anatomy, IOCs, payloads2026-07-09
1The Hacker News — NK Publish 108 Malicious Packages (PolinRider)Naming, counts, ecosystem scope2026-07-09
2SecurityWeek — NK Target Open Source DevelopersCorroboration of framing + payload roles2026-07-09
2Developer-Tech — PolinRider expands to PackagistPackagist expansion, takeover mechanics2026-07-09
2Socket — PolinRider live trackerFull live package/artifact inventory2026-07-09