WaterPlum's Contagious Interview: A 30,000-Device, $10.7M DPRK Crypto Heist

·

On September 18, 2026, cyber authorities in Japan, the United States, Australia and Germany released a joint advisory attributing a sprawling financial-theft operation to WaterPlum, a threat cluster the agencies link to North Korea's 313 General Bureau inside the Munitions Industry Department. The operation is the same one the security industry has tracked for two years as Contagious Interview.

Between December 2025 and July 2026, WaterPlum compromised at least 30,000 devices across more than 100 countries, targeted over 7,000 cryptocurrency wallets, and moved roughly 1.7 billion JPY — about $10.71 million USD in stolen crypto back to the regime. The entry point was not a firewall zero-day. It was a job offer.

This report breaks down the full kill chain: the recruiter lure, the BeaverTail loader, the InvisibleFerret Python backdoor, the OtterCookie/OtterCandy stealers and the StoatWaffle VS Code implant — plus IOCs, host and network hunt queries in KQL and SPL, ATT&CK mapping and the hardening that actually stops it.

◈ Table of Contents

01 Threat Actor Profile 02 Campaign Timeline 03 Initial Access — The Interview Lure 04 Malware Technical Deep Dive 05 Post-Exploitation & Lateral Movement 06 C2 Infrastructure 07 DFIR Investigation Steps 08 Indicators of Compromise 09 Detection & Hunt Queries 10 MITRE ATT&CK Mapping 11 Mitigation & Hardening 12 Sources & References
🎯

01 · Threat Actor Profile

WATERPLUM

WaterPlum is a financially motivated North Korean cluster whose sole strategic purpose is revenue generation for the sanctioned regime. Unlike espionage-first groups, its operators optimise for one metric: cryptocurrency out the door. The joint advisory is unusually direct in its attribution, naming the specific bureau responsible.

AttributeDetail
Primary nameWaterPlum
Campaign aliasContagious Interview (industry tracking); overlaps Unit 42 cluster CL-STA-0240 and the "Famous Chollima" job-lure activity set
AttributionNorth Korea — 313 General Bureau, Munitions Industry Department
ConfidenceHigh — joint government advisory (four nations, financial tracing)
MotivationFinancial theft (crypto) funding the DPRK regime; opportunistic IP theft / espionage as a secondary objective
First seenContagious Interview activity since 2023; this advisory covers Dec 2025 – Jul 2026
TargetingSoftware developers, web freelancers, crypto / NFT / AI professionals worldwide
Reach30,000+ devices, 100+ countries, 7,000+ wallets, ~$10.71M stolen
👤

Who Gets Targeted

Developers and freelancers with crypto wallets on their workstations — the exact population that routinely clones repos and runs untrusted code as part of a "test".

💸

The End Goal

Drain hot wallets, browser wallet extensions and seed phrases; launder proceeds back to the regime. The advisory traced ~$10.7M in a single 8-month window.

🏢

The Bonus Prize

When a victim is an employee, WaterPlum pivots from the personal device into the employer's network for source code and IP theft.

🗓️

02 · Campaign Timeline

DEC 2025 → SEP 2026
T
Disclosure & Activity Timeline
Chronology
  • 12023 onward: The "Contagious Interview" pattern emerges — DPRK operators pose as recruiters to push trojanised coding tasks to developers.
  • 2Dec 2025: The activity window covered by the joint advisory begins; WaterPlum scales delivery across recruiting and freelance platforms.
  • 3Dec 2025 – Jul 2026: 30,000+ devices infected across 100+ countries; 7,000+ wallets targeted; ~1.7B JPY / $10.71M moved to the regime.
  • 4Sep 18, 2026: Japan's National Police Agency, the US FBI/IC3, the Australian Cyber Security Centre and Germany's BfV publish a joint advisory naming WaterPlum and the 313 General Bureau.
  • 5Sep 19, 2026: Public reporting amplifies the advisory; defenders begin large-scale hunts for the malware families and C2 patterns described below.

The eight-month figure is the traced loss. Job-lure theft is chronically under-reported because victims are often individual freelancers, not enterprises with an IR retainer. Treat $10.7M as a floor, not a ceiling.

🪝

03 · Initial Access — The Interview Lure

SOCIAL ENGINEERING

WaterPlum weaponises the one workflow developers never question: cloning a repository and running it. There is no exploit and no CVE — the victim is walked into executing the payload by hand, believing it is a technical assessment.

01
Approach & Pretext
T1566.003

Operators impersonate legitimate AI, cryptocurrency and NFT companies, or reach out through recruiting and freelance marketplaces. The profile, company and role look real; the conversation moves quickly to a "take-home task" or a live coding session.

The Lure Sequence
  • 1Recruiter contacts the target on LinkedIn-style platforms, freelance boards, or a spoofed company careers channel.
  • 2The "assessment" is delivered as a Git repository, an npm project, or a Visual Studio Code workspace the candidate must run locally.
  • 3If the target hesitates, operators pivot to a "fix your video call" pretext — the candidate is told to run a command or install a component to resolve a fake conferencing glitch.
  • 4Running the project executes obfuscated JavaScript (BeaverTail) or a malicious VS Code task (StoatWaffle), and the chain begins.

The malicious code is frequently buried inside a legitimate-looking dependency or a .vscode/tasks.json "build" step — not the file the candidate is asked to review. Reading index.js is not enough.

Figure 1 — WaterPlum / Contagious Interview end-to-end flow
🔬

04 · Malware Technical Deep Dive

5 FAMILIES

WaterPlum runs a modular toolkit. BeaverTail is the JavaScript first stage; it pulls down a portable Python runtime and the InvisibleFerret backdoor; OtterCookie/OtterCandy and StoatWaffle are alternate or complementary stealers. The mechanics below are drawn from Unit 42's tracking of the same cluster (CL-STA-0240).

A
BeaverTail — JavaScript Loader & Wallet Stealer
Stage 1

BeaverTail is obfuscated JavaScript shipped inside npm packages on GitHub. Because cloning a repo and running Node is a routine, sanctioned developer action, execution rarely trips behavioural alarms. Its job is to steal browser wallet data immediately and then stage the second-stage backdoor.

What It Targets
  • 1Browser wallet extensions: MetaMask, Phantom, Coinbase, Binance and Crypto.com.
  • 2Solana keypairs on disk: ~/.config/solana/id.json.
  • 3Browser credential and cookie stores for session theft.
Observed staging artefacts (BeaverTail)
# helper / native module dropped to the profile root %USERPROFILE%\store.node # portable Python runtime staged for stage 2 %TEMP%\p.zi → extracted to %HOMEPATH%\.pyp\ # InvisibleFerret loader script written to the home dir %HOMEPATH%\.npl (or ~/.npl on macOS/Linux)

The .pyp and .npl dot-paths in a user's home directory are a high-fidelity tell. A legitimate toolchain does not ship a private Python interpreter into %HOMEPATH%\.pyp\python.exe.

B
InvisibleFerret — Python Backdoor
Stage 2

InvisibleFerret is the persistent workhorse: a Python backdoor launched by the staged interpreter. It fetches additional components, keylogs, harvests browsers, exfiltrates files over FTP and can drop remote-access software for hands-on-keyboard access.

Execution pattern
C:\Users\<user>\.pyp\python.exe C:\Users\<user>\.npl
Second-stage component fetch
GET http://<c2>:1224/payload/<campaign_id> → .n2/pay GET http://<c2>:1224/bow/<campaign_id> → .n2/bow (non-macOS)

InvisibleFerret installs Python dependencies to power its capabilities — pyWinhook (keylogging), pyperclip (clipboard), psutil (process control) and pywin32. Its command handlers are prefixed ssh_:

CommandFunction
ssh_cmdSession management
ssh_objRemote command execution
ssh_clipKeylogger / clipboard exfiltration
ssh_runDeploy browser stealer module
ssh_uploadFTP-based data exfiltration to actor servers
ssh_killTerminate Chrome / Brave processes (to unlock stores)
ssh_anyDownload and execute an AnyDesk binary
ssh_envBulk-harvest Documents / Downloads folders

Fetched components are obfuscated: the first eight bytes are a decode key, the remainder is Base64 then run through an XOR loop. Static signatures on the raw payload will miss it — hunt on the delivery paths and C2 port instead.

C
OtterCookie & OtterCandy — Stealer / RAT Hybrids
Alt stealer

OtterCookie is a JavaScript RAT-plus-infostealer delivered through malicious npm packages. OtterCandy combines OtterCookie with additional RAT features. Together they cover the same objective as the BeaverTail chain when a different delivery vehicle is used.

Collection scope
  • 1Browser authentication data and saved credentials.
  • 2Clipboard contents and live keystrokes.
  • 3Screenshots of the active session.
  • 4Cryptocurrency private keys and seed phrases.
  • 5Documents from local and shared folders.
D
StoatWaffle — Modular VS Code Implant
Node.js

StoatWaffle is a modular Node.js malware family delivered through malicious Visual Studio Code projects. It bundles credential harvesting, loader functionality and RAT capability, and it triggers when the victim opens or builds the "assessment" workspace.

Behavioural indicator — inspect the workspace, not the app code
# A weaponised VS Code project hides execution in a task definition: .vscode/tasks.json → "runOptions": {"runOn": "folderOpen"} + a node/curl command

VS Code's Restricted Mode blocks automatic task execution and workspace trust prompts. Opening any candidate's project in Restricted Mode neutralises the StoatWaffle folderOpen trick.

↔️

05 · Post-Exploitation & Lateral Movement

HANDS-ON

WaterPlum's automated theft is only stage one. Where the victim is an employee — not just a freelancer with a personal wallet — operators escalate to interactive access and move toward the corporate network.

01
From Personal Device to Employer Network
Espionage
  • 1Interactive access: ssh_any pulls down AnyDesk, giving operators a legitimate remote-access tool for hands-on-keyboard control that blends into normal IT traffic.
  • 2Credential reuse: harvested browser and SSO credentials open the door to the employer's VPN, source control and cloud consoles.
  • 3IP theft: the advisory notes lateral movement into employers' networks for intellectual-property theft and espionage — a secondary but strategic objective.
  • 4Bulk exfiltration: ssh_env and ssh_upload sweep Documents/Downloads and push data out over FTP to actor-controlled servers.

Because the initial infection is on a personal device that later touches corporate systems, this campaign routinely bypasses EDR that is only deployed on managed endpoints. The BYOD / contractor gap is the exposure.

📡

06 · C2 Infrastructure

PORT 1224

The most durable network signature across this cluster is HTTP command-and-control on TCP port 1224 with a consistent set of URI paths. Every stage — DLL retrieval, Python download, loader fetch and data upload — rides the same port and scheme.

C2 URI scheme (HTTP over :1224)
POST /keys # stolen credential data POST /uploads # wallet data GET /node/<version> # native/DLL helper retrieval GET /pdown # portable Python payload GET /client/<campaign> # InvisibleFerret loader GET /payload/<campaign># stage-2 component (.n2/pay) GET /bow/<campaign> # stage-2 component (.n2/bow)
Hunting the infrastructure
  • 1Shodan / Censys: pivot on HTTP services exposed on the non-standard port 1224 that return the /pdown or /client paths.
  • 2Passive DNS: resolve the known C2 domain and rotate through hosting neighbours on the same VPS ranges listed in the IOC section.
  • 3Netflow: any internal host initiating outbound HTTP to :1224 is worth an immediate look — it is not a common application port.
🧭

07 · DFIR Investigation Steps

RESPONDER PLAYBOOK
01
Triage a Suspected Contagious-Interview Host
In order
  • 1Check the dot-paths: look for %HOMEPATH%\.pyp\python.exe, %HOMEPATH%\.npl, %USERPROFILE%\store.node and any .n2\pay / .n2\bow.
  • 2Process ancestry: confirm whether node.exe or a VS Code task spawned python.exe from the .pyp directory.
  • 3Network history: query proxy/firewall/EDR for outbound connections to port 1224 and the C2 domain/IPs.
  • 4Persistence & RAT: check for an unexpected AnyDesk install and for scheduled tasks / run keys re-launching .pyp\python.exe.
  • 5Wallet exposure: enumerate installed browser wallet extensions and whether ~/.config/solana/id.json was read; assume any seed phrase on the host is compromised.
  • 6Blast radius: if the device touched corporate systems, pull SSO/VPN sign-in logs for the user and hunt for credential reuse from new locations.
Confirmed compromise when
  • A private Python interpreter exists under .pyp\ and executed .npl
  • Outbound HTTP to :1224 with /keys, /uploads or /pdown paths is present
  • Any wallet extension store or Solana keyfile was accessed by node/python
  • An unsanctioned AnyDesk instance appeared after the "interview"
🧾

08 · Indicators of Compromise

IOC

Hashes and network IOCs below are from Unit 42's tracking of this cluster (CL-STA-0240 / Contagious Interview). Rotate them into your TIP and treat the host-artefact paths as durable, campaign-level indicators.

Host artefacts (paths)
ArtefactMeaning
%USERPROFILE%\store.nodeBeaverTail helper / native module
%TEMP%\p.ziStaged portable Python archive
%HOMEPATH%\.pyp\python.exeDropped Python interpreter (stage-2 runner)
%HOMEPATH%\.npl / ~/.nplInvisibleFerret loader script
.n2\pay · .n2\bowInvisibleFerret second-stage components
~/.config/solana/id.jsonTargeted Solana keypair file
.vscode/tasks.jsonStoatWaffle auto-run trigger (folderOpen)
BeaverTail — SHA256
09a508e99b905330a3ebb7682c0dd5712e8eaa01a154b45a861ca12b6af29f86 de42155e14a3c9c4d919316d6ba830229533de5063fcd110f53e2395ef3aa77a fc9bb03998a89524ce5a0f859feb45806983aa4feb5f4d436107198ca869ff6f
InvisibleFerret — SHA256
35434e903bc3be183fa07b9e99d49c0b0b3d8cf6cbd383518e9a9d753d25b672 ab198c5a79cd9dedb271bd8a56ab568fbd91984f269f075d8b65173e749a8fde c547b80e1026d562ac851be007792ae98ddc1f3f8776741a72035aca3f18d277
C2 infrastructure
IndicatorType
blocktestingto[.]comC2 domain
144.172.74[.]48 · 144.172.79[.]23C2 IPv4
167.88.168[.]152 · 167.88.168[.]24C2 IPv4
172.86.123[.]35C2 IPv4
45.61.129[.]255 · 45.61.130[.]0C2 IPv4
45.61.160[.]14 · 45.61.169[.]187C2 IPv4
TCP/1224C2 port (HTTP) — behavioural indicator

IP and domain IOCs rotate fast on cheap VPS ranges. The durable detections are the dot-path artefacts, the .pyp\python.exe → .npl execution chain and outbound HTTP to :1224. Weight your rules accordingly.

🔎

09 · Detection & Hunt Queries

KQL + SPL

Before running the hunts, confirm you are collecting the telemetry they depend on. WaterPlum is an endpoint-first campaign, so process and file events carry most of the signal, with proxy/netflow covering the C2 leg.

Data sourceWhat it catchesPriority
Process creation (Sysmon ID 1 / DeviceProcessEvents).pyp\python.exe → .npl chain, node/Code spawning PythonCritical
File events (Sysmon ID 11 / DeviceFileEvents)store.node, .npl, .n2\ drops, malicious tasks.jsonCritical
Network / DeviceNetworkEventsOutbound TCP/1224 beaconsHigh
Proxy / web logs (CommonSecurityLog)/pdown, /client, /keys, /uploads URI pathsHigh
Identity sign-in logs (Entra / SSO)Credential reuse from new locations after infectionMedium

Detection 1 — Staged Python interpreter running the InvisibleFerret loader. Finds python.exe launched from a .pyp home-directory path executing a .npl script.

KQL — Microsoft Defender / Sentinel
DeviceProcessEvents | where FileName =~ "python.exe" | where FolderPath has "\\.pyp\\" | where ProcessCommandLine has ".npl" | project Timestamp, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessFileName
SPL — Splunk
index=endpoint sourcetype=*Sysmon* EventCode=1 Image="*\\.pyp\\python.exe" CommandLine="*.npl*" | table _time host user Image CommandLine ParentImage

Detection 2 — Node or VS Code spawning a home-directory Python drop. Finds the hand-off from the BeaverTail/StoatWaffle stage to the staged interpreter.

KQL — Microsoft Defender / Sentinel
DeviceProcessEvents | where InitiatingProcessFileName in~ ("node.exe", "Code.exe") | where FolderPath has "\\.pyp\\" or ProcessCommandLine has_any (".npl", "\\.n2\\") | project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, ProcessCommandLine
SPL — Splunk
index=endpoint sourcetype=*Sysmon* EventCode=1 (ParentImage="*\\node.exe" OR ParentImage="*\\Code.exe") (Image="*\\.pyp\\*" OR CommandLine="*.npl*" OR CommandLine="*\\.n2\\*") | stats count by host user ParentImage Image CommandLine

Detection 3 — Outbound C2 on port 1224. Finds the non-standard-port HTTP beacon shared across every stage of the chain.

KQL — Microsoft Defender / Sentinel
DeviceNetworkEvents | where RemotePort == 1224 | where InitiatingProcessFileName in~ ("node.exe", "python.exe", "Code.exe") | summarize hits=count(), paths=make_set(RemoteUrl, 10) by DeviceName, RemoteIP, InitiatingProcessFileName
SPL — Splunk
index=network (dest_port=1224) | search process IN ("node.exe","python.exe","Code.exe") | stats count values(dest_ip) values(url) by host process

Detection 4 — Known C2 URI paths in web/proxy logs. Finds the distinctive /pdown, /client and /keys request paths even if the port varies.

KQL — Sentinel (CommonSecurityLog / proxy)
CommonSecurityLog | where RequestURL has_any ("/pdown", "/client/", "/payload/", "/bow/", ":1224/keys", ":1224/uploads") | project TimeGenerated, SourceIP, DestinationHostName, RequestURL, DeviceAction
SPL — Splunk (proxy)
index=proxy (url="*/pdown*" OR url="*/client/*" OR url="*/payload/*" OR url="*/bow/*" OR url="*:1224/keys*") | table _time src dest url action

Detection 5 — Malicious VS Code auto-run task (StoatWaffle). Finds workspace tasks configured to execute a shell/node command on folder open.

KQL — Defender (file + process correlation)
DeviceFileEvents | where FolderPath endswith "\\.vscode\\tasks.json" | join kind=inner ( DeviceProcessEvents | where InitiatingProcessFileName =~ "Code.exe" | where ProcessCommandLine has_any ("curl", "powershell", "node -e", "Invoke-WebRequest") ) on DeviceId | project Timestamp, DeviceName, FolderPath, ProcessCommandLine
SPL — Splunk
index=endpoint sourcetype=*Sysmon* EventCode=1 ParentImage="*\\Code.exe" (CommandLine="*curl*" OR CommandLine="*powershell*" OR CommandLine="*node -e*" OR CommandLine="*Invoke-WebRequest*") | table _time host user CommandLine

Baseline first: legitimate developers do run node, python and VS Code tasks constantly. The signal is the combination — a dot-path interpreter, a .npl/.n2 artefact, and port 1224 — not any single event.

🗺️

10 · MITRE ATT&CK Mapping

TTPs
TacticTechniqueID
Initial AccessPhishing: Spearphishing via ServiceT1566.003
Initial AccessSupply Chain: Compromise Software DependenciesT1195.001
ExecutionUser Execution: Malicious FileT1204.002
ExecutionCommand & Scripting: JavaScriptT1059.007
ExecutionCommand & Scripting: PythonT1059.006
Credential AccessCredentials from Web BrowsersT1555.003
Credential AccessSteal Web Session CookieT1539
CollectionInput Capture: KeyloggingT1056.001
CollectionClipboard DataT1115
CollectionScreen CaptureT1113
CollectionData from Local SystemT1005
Command & ControlApplication Layer Protocol: Web ProtocolsT1071.001
Command & ControlIngress Tool TransferT1105
Command & ControlRemote Access Software (AnyDesk)T1219
ExfiltrationExfil Over Alternative Protocol (FTP)T1048
ImpactFinancial TheftT1657
🛡️

11 · Mitigation & Hardening

DEFEND
🧪

Run "Assessments" in a Throwaway VM

Never execute interview code, npm projects or VS Code workspaces on a device with wallets, SSO or corporate access. Use a disposable, network-isolated VM.

📦

Review Dependencies & Lockfiles

Inspect package.json, lockfiles and every transitive dependency before npm install. Watch for postinstall scripts that fetch remote code.

🔒

VS Code Restricted Mode

Open unfamiliar projects in Restricted Mode so tasks.json auto-run and workspace trust are disabled by default — this defeats StoatWaffle's folderOpen trigger.

🚫

Block Port 1224 Egress

There is no business reason for workstations to reach arbitrary hosts on TCP/1224. Block or alert on it at the proxy and firewall.

👛

Assume Wallet Compromise

If a host is even suspected, move funds to freshly generated wallets on a clean device and rotate every seed phrase — never re-import the old one.

🆔

Verify the Recruiter

Confirm applicant/recruiter identity, employer and role out-of-band. Treat "fix your video call by running this command" as an immediate red flag.

Response order matters: on a confirmed host, isolate the device and revoke sessions/credentials before resetting passwords — WaterPlum steals live cookies and can ride an active session through a password change.

📚

12 · Sources & References

REFERENCES
BleepingComputer — North Korean WaterPlum hackers infected 30,000 devices worldwide (Sep 19, 2026) The Register — North Korea's fake job interviews infected 30,000 devices (Sep 18, 2026) CyberPress — WaterPlum hackers infect 30,000 devices via fake job interviews Palo Alto Networks Unit 42 — Contagious Interview (CL-STA-0240): BeaverTail & InvisibleFerret technical analysis and IOCs FBI IC3 — Joint advisory index (Japan NPA · FBI/IC3 · ACSC · Germany BfV) MITRE ATT&CK — technique references

◈ Check your exposure to WaterPlum infrastructure

Run the C2 domain, IPs and file hashes from Section 08 through the CyberHawk IOC Scanner, then deploy the KQL/SPL hunts from Section 09 across your endpoint and proxy telemetry. For the latest DPRK job-lure tracking and detection content, follow CyberHawk Threat Intel and browse the blog.

◈ Stay Connected

Follow CyberHawk Threat Intel for threat intelligence, deployment guides and hands-on SOC tooling content.

🌐 Website ▶️ YouTube ▶️ YouTube (2) 𝕏 Twitter / X ♪ TikTok ✈️ Telegram
🔍 IOC Scanner 🛠️ Live Tools 📚 Courses 🚨 Threat Intel 📝 Blog 📋 SOPs

"They can't exploit you if you are the Exploit."