Threat Overview
A September 18 joint government advisory, reported September 19, attributes a large global campaign to North Korean-linked WaterPlum, also associated with Contagious Interview and Deceptive Development. Authorities report at least 30,000 infected devices in more than 100 countries and theft involving more than 7,000 cryptocurrency wallets. Attackers impersonate legitimate AI, cryptocurrency, and NFT companies or use recruiting and freelance platforms, then convince developers to download projects or execute malicious code. Malicious npm packages and developer tooling are recurring delivery mechanisms.
References
- BleepingComputer, September 19, 2026: https://www.bleepingcomputer.com/news/security/north-korean-waterplum-hackers-infected-30-000-devices-worldwide/
- Joint government and law-enforcement advisory by Japanese, U.S., Australian, and German authorities, September 18, 2026.
Impacted Systems
Developer workstations and endpoints that execute untrusted npm/Node.js projects or coding-test repositories. Windows, macOS, and Linux can be relevant depending on payload. This is social-engineering driven rather than a single vulnerable software version.
Why this matters
The campaign targets developers whose systems often contain source-code access, cloud credentials, SSH keys, registry tokens, and cryptocurrency assets.
Exploitation Status
Confirmed campaign. Authorities report activity from December 2025 through July 2026 and at least 30,000 compromised devices. The September 18 advisory is new intelligence; the tradecraft itself predates it.
What this hunt looks for
Developer tooling spawning shells, package-manager install scripts, unusual Node network activity, executable/script creation, credential-path interaction, and subsequent identity anomalies.
Required logs
Defender XDR DeviceProcessEvents, DeviceNetworkEvents and DeviceFileEvents, plus Microsoft Entra SigninLogs where available.
First Pass – Developer Tooling Spawning Shells
DeviceProcessEvents
| where Timestamp >= ago(30d)
| where InitiatingProcessFileName in~ ("code.exe","code","node.exe","node","npm.exe","npm","npx.exe","npx","yarn.exe","yarn","pnpm.exe","pnpm")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","bash","sh","curl","wget","python","python3")
| project Timestamp,DeviceName,AccountName,InitiatingProcessFileName,InitiatingProcessCommandLine,FileName,ProcessCommandLine,SHA256
| order by Timestamp descPackage Manager Install Scripts
DeviceProcessEvents
| where Timestamp >= ago(30d)
| where FileName in~ ("npm.exe","npm","npx.exe","npx","yarn.exe","yarn","pnpm.exe","pnpm","node.exe","node")
| where ProcessCommandLine has_any ("install","postinstall","preinstall","prepare")
| project Timestamp,DeviceName,AccountName,FileName,ProcessCommandLine,InitiatingProcessFileName,InitiatingProcessCommandLine
| order by Timestamp descDeveloper Tool Network Activity
DeviceNetworkEvents
| where Timestamp >= ago(30d)
| where InitiatingProcessFileName in~ ("node.exe","node","npm.exe","npm","npx.exe","npx","code.exe","code")
| project Timestamp,DeviceName,InitiatingProcessAccountName,InitiatingProcessFileName,InitiatingProcessCommandLine,RemoteUrl,RemoteIP,RemotePort
| order by Timestamp descNew Destinations From Node
let Known=DeviceNetworkEvents
| where Timestamp between (ago(30d)..ago(2d))
| where InitiatingProcessFileName in~ ("node.exe","node")
| summarize by DeviceId,RemoteUrl,RemoteIP;
DeviceNetworkEvents
| where Timestamp >= ago(2d)
| where InitiatingProcessFileName in~ ("node.exe","node")
| join kind=leftanti Known on DeviceId,RemoteUrl,RemoteIP
| project Timestamp,DeviceName,InitiatingProcessCommandLine,RemoteUrl,RemoteIP,RemotePort
| order by Timestamp descNode Creating Executables and Scripts
DeviceFileEvents
| where Timestamp >= ago(30d)
| where InitiatingProcessFileName in~ ("node.exe","node","npm.exe","npm","npx.exe","npx")
| where ActionType =~ "FileCreated"
| where FileName endswith ".exe" or FileName endswith ".dll" or FileName endswith ".ps1" or FileName endswith ".sh" or FileName endswith ".js"
| project Timestamp,DeviceName,FileName,FolderPath,SHA256,InitiatingProcessFileName,InitiatingProcessCommandLine
| order by Timestamp descCredential and Wallet Path Access
DeviceFileEvents
| where Timestamp >= ago(30d)
| where InitiatingProcessFileName in~ ("node.exe","node","python.exe","python","python3")
| where FolderPath has_any (".ssh",".aws",".azure","AppData\\Roaming","Library/Application Support")
| project Timestamp,DeviceName,ActionType,FileName,FolderPath,InitiatingProcessFileName,InitiatingProcessCommandLine
| order by Timestamp descRisky Entra Sign-Ins
SigninLogs
| where TimeGenerated >= ago(30d)
| where RiskLevelDuringSignIn !in~ ("none","hidden","")
| project TimeGenerated,UserPrincipalName,IPAddress,AppDisplayName,RiskLevelDuringSignIn,RiskState,RiskDetail,LocationDetails,DeviceDetail
| order by TimeGenerated descNew Sign-In IPs
let Known=SigninLogs
| where TimeGenerated between (ago(30d)..ago(2d))
| summarize by UserPrincipalName,IPAddress;
SigninLogs
| where TimeGenerated >= ago(2d)
| join kind=leftanti Known on UserPrincipalName,IPAddress
| project TimeGenerated,UserPrincipalName,IPAddress,AppDisplayName,ClientAppUsed,ResultType,LocationDetails,DeviceDetail
| order by TimeGenerated descDetection Notes
Developer environments generate substantial Node and shell noise. Highest-value signals are unexpected shell execution, new external destinations, credential-file access, and subsequent identity anomalies. Exact payload IOCs can change across campaign waves.