Contagious Interview — BeaverTail / OtterCookie Developer-Workstation Hunt

DPRK recruitment-themed phishing → actor GitHub repos → cross-platform loaders → crypto-wallet & credential theft. Aliases: Famous Chollima, Void Dokkaebi, HexagonalRodent (Lazarus cluster).
Threat
Contagious Interview
Severity
ACTIVE — NATION-STATE
Type
Supply-chain & social-eng
Primary Access
Phishing → dev repo execution
Version
v0.2 · 2026-06-25
Author
HuntPack
Confidence
High (multi-source)
01

Executive Summary

Contagious Interview is a North Korean (DPRK) threat cluster — tracked as Famous Chollima / Void Dokkaebi / HexagonalRodent under the Lazarus umbrella — that has industrialized its 2022-era fake-interview social engineering into a high-volume supply-chain operation. In its June 2026 evolution the actor sends recruitment-themed phishing emails (250+ emails in a six-week window to individuals at nearly 100 organizations in finance, crypto, education and tech) that link to actor-controlled GitHub repositories (e.g. the stardev0914 account). Victims — often mid-assessment job candidates — clone the repo, open it in VS Code or Cursor, and approve the editor's "trust the authors" prompt. That trust authorizes automatic execution of the project's tasks.json / postinstall hooks, which run OS-specific loaders (shell script on macOS/Linux, VBScript on Windows) that fetch the next stage from Vercel-hosted infrastructure.

The delivered payloads are the BeaverTail first-stage stealer/loader and the OtterCookie beaconing agent (both increasingly launched by GenAI-assisted loaders such as XORIndex/HexEval), pulling in the InvisibleFerret backdoor as a later stage. The objective is theft: browser-extension and desktop cryptocurrency wallets, browser credential stores, and clipboard contents. The campaign has seeded 1,700+ malicious packages across npm, PyPI, Go, Rust and Packagist, exfiltrated 26,584 crypto wallets from 2,726 infected developer systems, and stolen roughly $12M in cryptocurrency in the first three months of 2026.

The decisive defensive angle is developer-workstation behavior, not atomic IOCs (packages and repos rotate hourly). The unmistakable signal is a code editor (Code.exe / cursor / node / npm) spawning a network-download utility (curl, powershell, wscript, bash -c curl) immediately after a repo is opened, followed by access to browser wallet-extension directories and an outbound connection to hardcoded IP-based HTTP infrastructure.

Defender priority: Hunt for npm / node / VS Code (Code.exe) / Cursor processes spawning curl, powershell -enc, wscript, or osascript on developer endpoints, then pivot any hit to browser-wallet-directory reads and outbound connections to raw IPv4 HTTP endpoints (e.g. 23.137.105[.]75:5173). Treat editor-triggered network downloads on a workstation as guilty until proven benign.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Fwd
1The Hacker News — "NK hackers turning..." (Jun 2026)Industrialized phishing → GitHub repos; Overlord Go framework; BeaverTail/OtterCookie/InvisibleFerret; VS Code runOn:folderOpen; C2 23.137.105[.]75:5173; git-hook & postinstall persistence; $12M / 26,584 wallets / 2,726 systemsyes
1The Hacker News — "N-Korean spread 1,700 packages" (Apr 2026)Named npm/PyPI/Go/Rust/Packagist package IOCs; XORIndex → BeaverTail → InvisibleFerret chain; hardcoded IP HTTP exfil endpointyes
1Infosecurity-Magazine — Contagious InterviewXORIndex/HexEval loaders, npm download volumes, Lazarus attribution, BeaverTail second-stage / InvisibleFerret third-stagepartial
2Microsoft Security Blog — Contagious Interview (Mar 2026)Fake-interview delivery, VS Code trust-prompt abuse, repo-open task executionpartial
2Silent Push / Security Affairs / Trend Micro (Void Dokkaebi)Front-company infrastructure; OtterCookie via 197+ npm packages; stardev0914 GitHub account; Vercel payload stagingpartial
DarkReading — npm package factoryHTTP 403 at fetch time; corroborated by THN/Infosec coverage insteadno

Hunter note: Atomic package names and GitHub repos rotate continuously (1,700+ packages, 18+ npm accounts). Detection weight is placed on the behavioral chain; named packages are included as point-in-time blocklist/hunt seeds with appropriate expiry, every one traced to a cited source.

03

Hunt Brief & Attack Chain

Hunt hypotheses (fidelity-ordered)

  1. H1 (high): A code editor or package manager (Code.exe, cursor, node, npm, yarn, pnpm) spawns a network-download interpreter (curl, wget, powershell -enc, wscript, cscript, osascript) shortly after a repo is opened. (T1059, T1204.002)
  2. H2 (high): npm/node executes a lifecycle/postinstall hook that immediately reaches an external host or raw IPv4 endpoint. (T1195.002, T1071.001)
  3. H3 (high): A developer-tool process tree connects outbound to a hardcoded IP-literal HTTP endpoint on a non-standard port (e.g. :5173) — BeaverTail/InvisibleFerret exfil. (T1071.001, T1041)
  4. H4 (medium): A non-browser process reads browser wallet-extension or Login Data directories (MetaMask/Phantom IDs, Local Extension Settings, Login Data). (T1555.003, T1539, BeaverTail wallet theft)
  5. H5 (medium): Windows persistence via Run-key or Scheduled Task pointing at node/python/wscript in a user profile/Temp path; macOS/Linux persistence via git hooks. (T1547.001, T1053.005)
  6. H6 (medium): python loads or writes Cython-compiled .pyd/.so modules (InvisibleFerret migration) or executes from a cloned-repo path. (T1059.006, T1027)
  7. H7 (low): Outbound to Vercel/GitHub raw content fetching an executable payload from within a developer process tree. (T1105)

Attack chain

#StepTelemetryHunt Angle
1Recruitment phishing email links to actor GitHub repoMail gateway / proxy / DnsRequestGitHub clone URLs in email; DNS to actor repos
2Victim clones repo, opens in VS Code / Cursor, approves "trust authors"ProcessRollup2 (git/Code.exe/cursor)git clone followed by editor launch on fresh path
3tasks.json / postinstall runs OS loader (sh / vbs / node)ProcessRollup2 parent=editor/npmEditor or npm spawning curl/wscript/powershell (H1,H2)
4Loader fetches BeaverTail/OtterCookie from VercelNetworkConnectIP4 / DnsRequestDev process → external download (H7)
5BeaverTail enumerates & steals browser-wallet + credential storesProcessRollup2 / file accessNon-browser read of wallet/Login Data dirs (H4)
6Exfil + tasking to hardcoded IP HTTP C2 (e.g. :5173)NetworkConnectIP4IP-literal HTTP on odd port from dev tree (H3)
7Persistence (Run key / Scheduled Task / git hooks) + InvisibleFerretAsepValueUpdate / ProcessRollup2Run-key/task to node/python in Temp (H5,H6)
04

Consolidated IOC Table

Provenance: every atomic IOC below traces to a cited Tier-1 source (see §18). Package indicators are point-in-time and high-churn — use as hunt/pivot seeds, not standing blocks, and expire on schedule.

TypeValueConfActionContext / Source
IPv4 + port23.137.105[.]75:5173mediumhuntBeaverTail/InvisibleFerret C2 — THN Jun 2026
GitHub accountstardev0914mediumpivotActor repo/payload delivery — Trend/Silent Push
Staging host*.vercel.app (actor-controlled subdomains)lowenrichPayload fetch stage — Void Dokkaebi reporting
npm pkgdev-log-core, logger-base, logkitx, pino-debugger, debug-fmt, debug-glitzmediumhunt1,700-package campaign — THN Apr 2026
npm pkgredeem-onchain-sdk, nicegui, period-newline, terminal-logger-utils, terminal-logger-pack, js-logger-pack, node-env-resolve, sleek-prettymediumhuntnpm package factory — THN Jun 2026
PyPI pkglogutilkit, apachelicense, fluxhttp, license-utils-kitmediumhunt1,700-package campaign — THN Apr 2026
Go pkggithub[.]com/golangorg/formstash, github[.]com/aokisasakidev/mit-license-pkgmediumhuntGo ecosystem — THN Apr 2026
Rust / Packagistlogtrace (crate); golangorg/logkit, roberts/leads (Packagist)mediumhuntCross-ecosystem — THN Apr/Jun 2026
Loader namesXORIndex, HexEval (npm loaders)mediumenrichLoads BeaverTail — Infosecurity-Mag
Malware familyBeaverTail, OtterCookie, InvisibleFerrethighenrichNamed payloads — all Tier-1 sources
Behavior tokenstasks.json runOn:folderOpen, postinstall, .githooks/pre-commithighhuntExecution/persistence — THN + MSFT
05

Affected Surface & Telemetry Matrix

SurfaceRequired TelemetryPriorityGap Risk
Developer workstations (Win/macOS/Linux)ProcessRollup2 / SyntheticProcessRollup2criticallow — core sensor
Outbound C2 / payload fetchNetworkConnectIP4, DnsRequesthighlow
Browser wallet / credential storesProcessRollup2 cmdline + file-access contexthighmed — file-read granularity varies
Persistence (Run key / Sched task)AsepValueUpdate, RegGenericValueUpdate, ProcessRollup2 (schtasks)mediumlow
macOS/Linux git-hook persistenceProcessRollup2 (sh/bash spawned by git)mediummed — non-Win EDR coverage
Email deliveryMail gateway / proxy logs (non-Falcon)mediumhigh — out-of-band
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
Initial AccessT1566.002 Phishing: LinkRecruitment email links to actor GitHub repoNative §7 / Hardening §12
Initial AccessT1195.002 Supply Chain: Software DependenciesMalicious npm/PyPI/Go/Rust packagesQ2 / IOA
ExecutionT1204.002 User Execution: Malicious FileRepo opened in VS Code/Cursor; trust prompt approvedQ1
ExecutionT1059.001/.003/.006/.007 Command & Scriptingpowershell / cmd / python / vbscript loadersQ1, Q2, Q6
Command & ControlT1071.001 Application Layer: WebHTTP to hardcoded IP endpoint :5173Q3
Ingress Tool TransferT1105Loader fetches payload from Vercel/GitHubQ3, Q7
Credential AccessT1555.003 Credentials from Web BrowsersRead of Login Data / browser storesQ4
Credential AccessT1539 Steal Web Session CookieBrowser cookie/session theftQ4
CollectionT1115 Clipboard DataClipboard wallet-address monitoringIOA / §7
PersistenceT1547.001 Run KeysRun-key to node/python/wscript in TempQ5
PersistenceT1053.005 Scheduled TaskScheduled task launching loaderQ5
ExfiltrationT1041 Exfil Over C2 ChannelWallets/creds sent to IP C2Q3
07

Native Audit-Log Hunts (non-CQL)

  • Mail gateway / proxy: Hunt inbound mail with body links to github.com/<new-account> clone URLs paired with recruiter/assessment language ("coding assessment", "take-home", "interview task"). Flag first-seen GitHub orgs.
  • npm/registry telemetry: If you run an internal registry or Artifactory/Verdaccio, alert on installs of the §4 package names and on first-seen packages with postinstall scripts that perform network I/O.
  • VS Code / Cursor config: On managed dev fleets, audit for repos containing .vscode/tasks.json with "runOptions": {"runOn": "folderOpen"} and for .githooks/pre-commit shipped inside cloned repos.
  • macOS Unified Log / Linux auditd: Hunt osascript/curl/bash -c children of node, Code Helper, or cursor; and git core.hooksPath changes.
  • Endpoint DNS: Review resolutions to freshly-registered *.vercel.app subdomains from developer hosts immediately preceding executable writes.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · Code editor / package manager spawning a download interpreter
CONF HIGHFP MEDCOST LOW

Looks for: VS Code / Cursor / node / npm spawning curl, wget, powershell, wscript, cscript, mshta, bash or osascript — the repo-open task-execution step. FP: legitimate build scripts and npm installs that shell out; tune with the C2/raw-IP pivot (Q3) and exclude known internal build hosts.

// HUNT: Editor/pkg-mgr spawns download interpreter (Contagious Interview repo-open)
// MITRE: T1204.002, T1059  | CONF: high  FP: med  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: build tooling, legitimate npm postinstall, CI agents
// TUNING: exclude known build/CI hosts; corroborate with Q3 raw-IP C2
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^(Code\.exe|Code - Insiders\.exe|cursor\.exe|cursor|Code Helper.*|node|node\.exe|npm|npm\.cmd|yarn|pnpm)$/i
| FileName=/^(curl|wget|powershell|pwsh|wscript|cscript|mshta|bitsadmin|certutil|bash|sh|osascript|python3?)(\.exe)?$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine], limit=200)
Q2 · npm/node postinstall lifecycle hook reaching the network
CONF HIGHFP MEDCOST LOW

Looks for: node/npm command lines invoking install lifecycle hooks combined with download verbs — the malicious-package install step. FP: legitimate packages with native postinstall builds; tune to user-profile / repo paths and pair with outbound-IP correlation.

// HUNT: npm/node lifecycle hook with inline network fetch (malicious dependency)
// MITRE: T1195.002, T1059.007  | CONF: high  FP: med  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: native-module builds (node-gyp) that download toolchains
// TUNING: scope to user-writable repo/Temp paths; exclude vetted internal pkgs
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/(postinstall|preinstall|prepare)/i
| CommandLine=/(curl\s|wget\s|Invoke-WebRequest|iwr\s|http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|fetch\(|child_process)/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine], limit=200)
Q3 · Dev process tree connecting to a raw IPv4 HTTP endpoint on an odd port
CONF HIGHFP MEDCOST MED

Looks for: outbound connections to public IP literals on non-standard ports (e.g. 5173) initiated by a developer-tool process — BeaverTail/InvisibleFerret exfil/tasking, including the documented 23.137.105.75:5173. FP: local dev servers (Vite default 5173) on RFC1918 — query already excludes private ranges; tune to retain only public destinations.

// HUNT: developer process beaconing to public IP literal on odd port (C2 exfil)
// MITRE: T1071.001, T1041, T1105  | CONF: high  FP: med  COST: med
// REQUIRES: NetworkConnectIP4 (+ ProcessRollup2 for tree context)
// FALSE POSITIVES: local Vite dev server (5173) on private IPs — excluded below
// TUNING: confirm RemoteAddressIP4 is public; pin RemotePort to observed C2 ports
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^(node|node\.exe|npm|curl|curl\.exe|powershell\.exe|pwsh\.exe|python3?|Code\.exe|cursor|wscript\.exe)$/i
| RemoteAddressIP4!=/^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.|169\.254\.)/
| in(field="RemotePort", values=[5173, 1224, 3000, 8080, 8000, 5000])
| groupBy([aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort], function=count())
| sort(_count, order=desc)
Q4 · Non-browser process accessing browser wallet / credential stores
CONF MEDFP MEDCOST LOW

Looks for: command lines referencing browser wallet-extension IDs / Login Data / Local Extension Settings from non-browser processes — BeaverTail wallet & credential theft. FP: backup/security tooling and password managers reading the same paths; exclude known-good readers by ImageFileName.

// HUNT: wallet-extension / browser credential store access by non-browser proc
// MITRE: T1555.003, T1539  | CONF: med  FP: med  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: backup agents, password managers, browser self-access
// TUNING: exclude vetted backup/PWM ImageFileNames; require dev-tool parent
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/(Local Extension Settings|Login Data|nkbihfbeogaeaoehlefnkodbefgpgknn|bfnaelmomeimhlpmgjnjophhpkkoljpa|Solflare|metamask|Wallets|keystore)/i
| ImageFileName!=/\\(chrome\.exe|msedge\.exe|firefox\.exe|brave\.exe|opera\.exe)$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine], limit=200)
Q5 · Persistence — Run key / Scheduled Task to interpreter in user/Temp path
CONF MEDFP MEDCOST LOW

Looks for: Run-key/ASEP writes referencing node/python/wscript/curl out of a user-profile or Temp path. FP: legitimate dev tools that self-register at login; tune to Temp/Downloads/cloned-repo paths only.

// HUNT: Run-key persistence pointing at interpreter in user/Temp path
// MITRE: T1547.001, T1053.005  | CONF: med  FP: med  COST: low
// REQUIRES: AsepValueUpdate / RegGenericValueUpdate
// FALSE POSITIVES: dev tools registering legitimate startup entries
// TUNING: restrict RegStringValue to Temp/Downloads/AppData repo paths
#event_simpleName=/AsepValueUpdate|RegGenericValueUpdate/
| RegObjectName=/\\Software\\Microsoft\\Windows\\CurrentVersion\\Run/i
| RegStringValue=/(node|python|pythonw|wscript|cscript|curl|powershell).*(\\Temp\\|\\Downloads\\|\\AppData\\)/i
| table([@timestamp, aid, ComputerName, RegObjectName, RegValueName, RegStringValue], limit=200)
Q6 · Python executing from a cloned-repo / Temp path (InvisibleFerret stage)
CONF MEDFP HIGHCOST LOW

Looks for: python invoked against scripts under Temp/Downloads/AppData with download or base64 verbs — InvisibleFerret loader behavior. FP: developers legitimately run python from working dirs; this is a high-FP triage hunt — always pair with Q3/Q4 hits before escalating.

// HUNT: python from user/Temp path with network/encoding verbs (InvisibleFerret)
// MITRE: T1059.006, T1027, T1105  | CONF: med  FP: high  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: normal developer python usage from project dirs
// TUNING: only triage when correlated with a Q3 C2 hit on the same aid
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ImageFileName=/(python3?|pythonw?\.exe)$/i
| CommandLine=/(\\Temp\\|\\Downloads\\|\\AppData\\|\/tmp\/).*(urllib|requests|base64|socket|http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine], limit=200)
Q7 · git clone immediately followed by editor launch + download (chain stitch)
CONF MEDFP MEDCOST MED

Looks for: a host that runs git clone and, within the same window, an editor or interpreter spawning a download utility — stitches the lure-to-execution chain per host. FP: normal clone-then-build workflows; rank by hosts where the download target is a public IP literal.

// HUNT: git clone then editor/interpreter download on same host (lure chain)
// MITRE: T1204.002, T1105  | CONF: med  FP: med  COST: med
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: legitimate clone-and-build developer workflows
// TUNING: prioritize hosts whose download verb targets a public IP literal
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| case {
    CommandLine=/git\s+clone/i | marker := "clone";
    ParentBaseFileName=/^(Code\.exe|cursor|node|npm)$/i AND ImageFileName=/(curl|wget|powershell\.exe|wscript\.exe|bash|osascript)/i | marker := "exec";
    * | marker := "other";
  }
| marker!="other"
| groupBy([aid, ComputerName], function=collect([marker, CommandLine, ImageFileName], limit=20))
| CommandLine=/clone/ OR marker="exec"

Field note: All fields above are standard Falcon data-model fields. RemotePort values and the C2 port list reflect observed/likely Contagious Interview tasking ports — confirm port pins against your tenant's own dev traffic baseline before promoting Q3 to an alert.

09

CrowdStrike Custom IOA Recommendations

IOA NamePattern (process)ActionExclusions
CI — Editor spawns downloaderParent Code.exe|cursor|node|npm → child curl|wscript|powershell|osascriptDetect (Critical) on dev OU; monitor elsewhereCI/build agents, known internal build hosts
CI — postinstall network fetchCmdLine contains postinstall + (curl|iwr|IP literal)Detect (High)Vetted native-module packages (node-gyp)
CI — IP-literal C2 from dev toolnode/curl/python → public IPv4 HTTP on 5173/odd portDetect (High)Private-range Vite/dev servers (already excluded)
CI — wallet store read by non-browserNon-browser proc CmdLine references wallet ext IDs / Login DataDetect (Medium)Approved backup / password-manager binaries

Promote Q1, Q2 and Q3 to Custom IOAs scoped to a developer host group first (lower FP blast radius), validate for one detection cycle, then widen. Q4–Q7 remain Investigate-only hunts pending tenant baselining.

10

Machine-Readable IOC Appendix

Grouped quick-copy blocks. Atomic indicators are point-in-time (cited §18); replace REPLACE_WITH_* placeholders only with hashes you confirm from a real source — do not fabricate.

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
ipv4,23.137.105.75,detect,high,2026-09-25,Contagious Interview BeaverTail/InvisibleFerret C2,campaign:ContagiousInterview
sha256,REPLACE_WITH_CONFIRMED_HASH,prevent,critical,2026-12-25,BeaverTail loader payload,campaign:ContagiousInterview
domain,REPLACE_WITH_CONFIRMED_VERCEL_SUBDOMAIN,detect,medium,2026-07-25,Actor payload staging,campaign:ContagiousInterview
Behavioral Signatureshunt seeds
# Parent-spawns-downloader (editor/npm => curl|wscript|powershell|osascript)
# npm/node lifecycle: postinstall|preinstall + (curl|iwr|IP-literal)
# Public IPv4 HTTP from node/curl/python on odd port (e.g. 5173)
# Non-browser read of: Login Data, Local Extension Settings, wallet ext IDs
# VS Code tasks.json runOn:folderOpen ; .githooks/pre-commit in cloned repo
Named Packages / Toolingregistry hunt
# npm
dev-log-core logger-base logkitx pino-debugger debug-fmt debug-glitz
redeem-onchain-sdk nicegui period-newline terminal-logger-utils
terminal-logger-pack js-logger-pack node-env-resolve sleek-pretty
# PyPI
logutilkit apachelicense fluxhttp license-utils-kit
# Go
github.com/golangorg/formstash github.com/aokisasakidev/mit-license-pkg
# Rust crate: logtrace | Packagist: golangorg/logkit roberts/leads
# Malware: BeaverTail OtterCookie InvisibleFerret | Loaders: XORIndex HexEval
Dev-Fleet Audit Checklistharden / verify
# VS Code: enable Workspace Trust (security.workspace.trust.enabled=true)
# Block tasks.json auto-run: security.workspace.trust.untrustedFiles=open
# npm: set ignore-scripts=true on dev images for untrusted clones
# Audit git config core.hooksPath on developer endpoints
# Egress: alert dev hosts => public IPv4-literal HTTP on non-standard ports
11

Detection Validation Gates

GateCheckPass criteria
Telemetry readyProcessRollup2 + NetworkConnectIP4 flowing from developer OU hosts>= 95% dev endpoints reporting in last 24h
Benign baselineRun Q1/Q2 over 7d; catalog legitimate editor-spawns-downloader on build/CI hostsKnown-good parents enumerated & excluded
Positive testIn a lab VM, open a benign repo whose tasks.json runs curl http://<test-ip>:5173Q1 and Q3 both fire on the test host
Wallet-read testRun a script reading a dummy Login Data path from non-browserQ4 fires; approved readers excluded
PromotionQ1/Q2/Q3 false-positive rate over one cycle on dev OU< 1 FP/host/week before IOA promotion
12

Hardening — Tiered & Deployable

Immediate (this week)
  • Enforce VS Code Workspace Trust (security.workspace.trust.enabled) and disable auto-task execution on untrusted folders — directly defeats the tasks.json runOn:folderOpen vector. (M1038 Execution Prevention; vendor guidance)
  • Set ignore-scripts=true in the developer-image npm config so cloned/untrusted packages cannot run postinstall automatically. (M1042 Disable/Remove Feature)
  • Block public IPv4-literal HTTP on non-standard ports from developer egress (proxy/firewall) — strips the BeaverTail C2 channel. (M1037 Filter Network Traffic)
  • Block §4 packages in the internal registry / dependency firewall as point-in-time hunt seeds. (M1042)
Near term (1–4 weeks)
  • User-awareness for recruitment lures targeting developers / crypto staff: never run interview "assessment" repos on a corporate endpoint. (M1017 User Training)
  • App control / WDAC on dev fleet to constrain interpreters (wscript, mshta, powershell) launched by editors. (M1038)
  • Browser-wallet-extension governance: inventory and, where policy allows, restrict crypto-wallet extensions on corporate browsers. (M1042)
Strategic (1–3 months)
  • Isolate developer build/test work in disposable VMs or remote dev containers, segmented from credential/wallet stores. (M1030 Network Segmentation)
  • Dependency-provenance enforcement (lockfile pinning, signature/SLSA checks, registry allow-listing) across all five ecosystems. (M1016 Supply Chain)
13

Deployable Playbooks

1 · VS Code Workspace Trust + disable folder-open tasks (managed settings.json)

// Deploy to %APPDATA%\Code\User\settings.json (Win) or
//   ~/Library/Application Support/Code/User/settings.json (macOS) via MDM/GPO
{
  "security.workspace.trust.enabled": true,
  "security.workspace.trust.startupPrompt": "always",
  "security.workspace.trust.untrustedFiles": "open",
  "task.allowAutomaticTasks": "off"
}

2 · Harden npm on developer images (disable lifecycle scripts for untrusted installs)

# System/user npmrc — block auto-run of pre/postinstall scripts
npm config set ignore-scripts true --global
# Verify
npm config get ignore-scripts        # expect: true
# For vetted internal builds, run install explicitly with scripts enabled:
#   npm ci --foreground-scripts --ignore-scripts=false   (in CI only)

3 · Audit git hooksPath across the dev fleet (PowerShell)

$paths = git config --show-origin --get-all core.hooksPath 2>$null
if ($paths) { Write-Warning "Non-default git hooksPath set:"; $paths }
else { Write-Output "core.hooksPath: default (no override)" }
# Flag any repo shipping its own .githooks/ + core.hooksPath override

4 · Egress block — dev hosts to public IP-literal HTTP on odd ports (firewall pseudo-rule)

# Proxy/NGFW: from DEV_WORKSTATION_GROUP
#   deny dst=PUBLIC and dst-port in {5173,1224,3000,8000,8080} and app=http/raw
#   log+alert matches; allowlist legitimate SaaS dev endpoints by FQDN only
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-contain the host in Falcon; preserve process tree & netconn timelineSOC L2aid, ComputerName, Q1/Q3 hits
EradicateKill loader/payload; remove Run-key/Sched-task/git-hook persistence; delete cloned repo + fetched payloadsIRQ5 reg entries, file paths
Credential resetAssume browser creds + crypto wallets compromised: rotate all browser-stored passwords, session tokens, and move funds from any wallet that touched the hostIR + userQ4 wallet-read hits
Hunt-widerPivot C2 IP across fleet; sweep dev OU with Q1–Q3; check internal registry for §4 packagesThreat HuntQ3 groupBy, registry logs
RecoverReimage host; restore from clean backup; re-enable after Workspace-Trust/npm hardening appliedITRebuild ticket
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1204.002 / T1059Editor spawns downloaderQ1, Q7YesGood
T1195.002Malicious package postinstallQ2YesGood
T1071.001 / T1041 / T1105IP-literal C2 / payload fetchQ3, Q7YesGood
T1555.003 / T1539Browser wallet / credential theftQ4YesPartial
T1547.001 / T1053.005Run-key / Sched-task persistenceQ5NoPartial
T1059.006 / T1027InvisibleFerret python/.pyd stageQ6NoPartial
T1115Clipboard wallet-address swapIOA onlyGAP
T1566.002Recruitment phishing link delivery— (mail GW)NoGAP — out of band
macOS/Linux git-hook persistencegit core.hooksPath abuse§7 / §13.3NoPartial

Known gaps: clipboard-hijack and email-delivery detection sit outside Falcon endpoint telemetry — cover via browser/clipboard EDR signals and the mail-gateway hunt in §7. Persistence and InvisibleFerret hunts (Q5/Q6) are present but unpromoted pending tenant baselining (see §11 gates). Validation: do not promote any query to a blocking IOA until its §11 gate passes.

16

Hunt Summary Ticket

TITLE......: Hunt — Contagious Interview (BeaverTail / OtterCookie) on developer endpoints
SEVERITY...: HIGH (active DPRK nation-state supply-chain + social engineering)
SCOPE......: Developer / engineering workstations (Win, macOS, Linux); crypto/finance staff
HYPOTHESIS.: Recruitment-phish => actor GitHub repo opened in VS Code/Cursor => tasks.json/
             postinstall loader => BeaverTail/OtterCookie => wallet+cred theft => IP C2 exfil
QUERIES....: Q1 editor-spawns-downloader | Q2 npm postinstall netfetch | Q3 IP-literal C2
             Q4 wallet/cred read | Q5 run-key persist | Q6 InvisibleFerret python | Q7 chain
DO FIRST...: Run Q1 + Q3 over 7d on dev OU; triage any host where both fire on same aid
FINDINGS...: <pending hunt execution>
GAPS.......: Clipboard hijack (T1115) + email delivery = out-of-band; Q5/Q6 unpromoted
ACTIONS....: Enforce VS Code Workspace Trust; npm ignore-scripts; block IP-literal egress;
             rotate browser creds + move wallet funds for any confirmed host
OWNER......: HuntPack / Threat Hunt
VERSION....: v0.1 · 2026-06-25
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-06-25Initial HuntPack — 7 CQL hunts, 4 Custom IOA recs, tiered hardening + playbooks, containment runbook. Behavioral-first; atomic IOCs (C2 IP, packages, GitHub account) sourced to Tier-1 reporting.
18

References

TierSourceUsed ForAccess Date
1THN — North Korean hackers turning (Jun 2026)Chain, C2 IP, persistence, Overlord, impact figures2026-06-25
1THN — 1,700 malicious packages (Apr 2026)npm/PyPI/Go/Rust/Packagist package IOCs; loader chain2026-06-25
1Infosecurity-Magazine — Contagious InterviewXORIndex/HexEval, attribution, payload staging2026-06-25
2Microsoft Security Blog (Mar 2026)VS Code trust-prompt / repo-open task execution2026-06-25
2Trend Micro — Void Dokkaebistardev0914 account, Vercel staging, OtterCookie2026-06-25
2DarkReading — npm package factoryCross-reference (403 at fetch; corroborated via THN)2026-06-25