← Dispatches

TLP:CLEAR · 2026-02-26

new Function(), New Threat: Inside a Live DPRK Developer-Targeting Campaign

We acquired live malware source code from a DPRK-linked campaign targeting software developers through trojanized Next.js coding assessments, then registered a fake agent with the live C2 and captured the full Stage 2 tasking payload on delivery. We deobfuscated it. Two C2 servers remain operational.

Two days after we published our analysis of a trojanized CUDA toolkit linked to DPRK threat actors, we found the same operational cluster has pivoted. The new vector: fake Next.js coding assessments sent to developers during job interviews. We pulled the actual malware loader source code off live staging infrastructure, confirmed two active command-and-control servers, registered a fake agent, and captured the full Stage 2 tasking payload when the C2 pushed it to our session. We deobfuscated the entire chain. The campaign is still running.

Discovery and Acquisition

Microsoft Defender published a report on February 24 identifying malicious repositories on Bitbucket posing as legitimate Next.js projects. We independently acquired artifacts from the live infrastructure and confirmed active C2 servers. Five Vercel-hosted staging domains remained live at time of analysis, including endpoints actively serving malware loader code to anyone who requested it.

The campaign uses recruiting-themed lures. A developer receives a repository framed as a technical assessment or interview project. The repo looks legitimate. It builds. It runs. It also phones home to Pyongyang.

Three Execution Paths, One Outcome

The attackers embedded three independent execution triggers into each repository, ensuring infection regardless of how the developer interacts with the code:

Path 1: VS Code Workspace Trigger

A .vscode/tasks.json file configured with runOn: "folderOpen" executes a Node script the moment the developer opens the project folder in VS Code and clicks Trust. The script fetches a loader from a Vercel staging domain and executes it in memory.

Path 2: Dev Server Trigger

When the developer runs npm run dev, a trojanized JavaScript file disguised as a legitimate library (e.g., jquery.min.js) decodes a base64 URL hidden in its source, fetches a loader from Vercel, and executes it within the running Node.js process.

Path 3: Backend Startup Trigger

The .env file contains a base64-encoded C2 endpoint stored in an innocuous variable like AUTH_API. A backend route file (server/routes/api/auth.js) decodes it on import, transmits the entire process.env to the attacker, and executes whatever JavaScript the server returns using new Function("require", response.data)(require). This path exfiltrates cloud API keys, database credentials, and deployment secrets before the malware even establishes persistence.

The Stage 1 Loader: Hiding RCE in Error Handling

We acquired the complete Stage 1 loader source code directly from two live Vercel endpoints. One served it in cleartext. The other used string array rotation obfuscation. Both implement the same logic. The cleartext variant, pulled from api-web3-auth[.]vercel[.]app/api/auth, is 1,290 bytes of weaponized simplicity:

The loader imports axios and os, then defines two functions. getSystemInfo() collects the victim\'s hostname, every non-null MAC address from all network interfaces, and the OS type, release, and platform. checkServer() sends this profile to the C2 as query parameters alongside an exceptionId (a per-campaign identifier like env101383) and an instanceId that starts at zero.

The C2 response determines what happens next. If status is "ok", the loader stores any returned instanceId for session tracking and loops. If status is "error", the message field is passed to errorFunction(), which calls new Function(\'require\', message) and immediately invokes the result with Node\'s require as the argument.

That single line is the entire RCE mechanism. new Function() compiles an arbitrary string into executable JavaScript at runtime. By passing require as a parameter, the attacker\'s code gains full access to Node\'s module system, meaning it can load child_process to run shell commands, fs to read and write files, net to open sockets, or anything else in the Node.js ecosystem. The server sends code. The victim\'s machine runs it. No files touch disk.

The loader polls every 5 seconds via setInterval(checkServer, 5000). It is patient. The C2 returns {"status":"ok","message":"server connected"} until an operator decides to engage. We confirmed this response from both live C2 servers during our analysis.

The Obfuscated Variant

The second loader, served from oracle-v1-beta[.]vercel[.]app/api/getMoralisData, implements identical logic through string array rotation obfuscation. We extracted the string table and reconstructed the C2 URL from fragments: http: + //163 + .245. + 194.2 + 16:30 + 00 resolves to 163[.]245[.]194[.]216:3000, one of the two C2 servers we confirmed as live. The obfuscation adds anti-debugging wrappers and a self-referencing toString trap, but the core logic, new Function(\'require\', message), remains identical.

Live C2 Infrastructure

We probed all four C2 IP addresses identified in the campaign. Two responded:

IPPortASNLocationStatus
147.124.202.2083000AS397423 Tier.NetNew York, USLIVE
163.245.194.2163000AS26666 InterserverDallas, USLIVE
87.236.177.93000AS207728 EUROHOSTERNetherlandsDOWN
66.235.168.1363000AS397423 Tier.NetOregon, USDOWN

Both live servers run Express.js (confirmed via X-Powered-By header) and expose two endpoints: GET /api/errorMessage returns the idle beacon response, and GET /health returns {"ok":true}. The servers accept any instanceId parameter without validation, returning the same static response. POST requests to all endpoints return 404, indicating the tasking endpoints (/api/handleErrors, /api/reportErrors) either use a different HTTP method signature or have been reconfigured since Microsoft\'s initial observation.

The ETag header W/"2c-C+DUpHd4h+gH5EGTBj3pzaIUfSY" is identical across both servers and has remained stable throughout our monitoring window, confirming a shared codebase deployed to separate infrastructure. No TLS is configured on either C2, all communication runs over plaintext HTTP.

Stage 2: Captured Live

After building a fake agent that mimicked the Stage 1 beacon protocol, we registered with the live C2 at 163[.]245[.]194[.]216:3000 and maintained a persistent session. On beacon 38, the server pushed a 19KB obfuscated JavaScript payload via {"status":"error","message":"<code>"}. Over the next two minutes, it delivered the same payload six more times, each with a unique handleCode tracking token that changed the hash on every delivery, per-session polymorphism designed to defeat hash-based detection.

We fully deobfuscated the Stage 2 tasking client by reversing the string array rotation and resolving the concatenation chain. The obfuscated wrapper builds a 4,928-character JavaScript string, then immediately spawns it as a detached, hidden child Node.js process: spawn(process.execPath, ["-e", code], {detached: true, windowsHide: true, stdio: ["pipe", "ignore", "ignore"]}).unref(). The parent process exits. The child persists.

The deobfuscated Stage 2 operates as a polling tasking client. It collects a system profile identical to Stage 1 (hostname, all MACs, OS details) plus the victim\'s public IP address via api[.]ipify[.]org. It then enters an infinite loop, POSTing {agentId, handleCode, sysInfo} to POST /api/handleErrors every 5 seconds with a 10-second timeout race.

The C2 response drives all behavior. A messages[] array contains arbitrary JavaScript strings, each spawned as a separate hidden child process via STDIN pipe, the same new Function() philosophy but now through child_process.spawn. A responseCode field rotates the session token on every exchange. A newAgentId field enables live agent ID rotation. A responseCode of "-1" triggers self-destruct: the client kills all managed child processes via taskkill /T /F on Windows or SIGTERM/SIGKILL on Unix, then exits.

Error telemetry is posted to POST /api/reportErrors with typed categories: deps-address for IP resolution failures, deps-child-main for child spawn errors, deps-running for execution failures, deps-main for C2 communication errors, and deps-theard for main loop crashes. The misspelling of "thread" as theard is a consistent fingerprint confirming a non-native English speaker authored the code.

Microsoft\'s earlier analysis identified additional Stage 2 capabilities including directory browsing through paired enumeration endpoints (/api/hsocketNext and /api/hsocketResult) and staged file exfiltration through a three-phase upload workflow (/upload, /uploadsecond, /uploadend). These endpoints are accessed through the messages[] tasking mechanism we captured, the C2 sends JavaScript that implements these capabilities, and the Stage 2 client blindly executes it. This is targeted espionage infrastructure, not spray-and-pray commodity malware.

The Social Engineering Layer: monobyte

The attackers built a complete fake product to justify the repository\'s existence. monobyte-code[.]vercel[.]app serves a polished landing page for a fictional code editor called monobyte, branded as "THE LAST EDITOR YOU NEED." The site features a retro CRT aesthetic with scanline effects, a lo-fi background music track titled "Neon Haze," and a feature list including "Smart indexing & debugging," "Agent Instructions manager," and "Code review & git GUI."

The page includes a Mailchimp waitlist form connected to an account under the name chaosbyte (Mailchimp datacenter: us9, list ID: a6193b602503875411d9c8e9a). This is likely harvesting developer email addresses for future targeting. The entire frontend is built with Vue.js 3.5.25 and Vite, not Next.js, meaning the attackers used a different framework for their bait site than the one they\'re trojanizing.

Victim Profiling: IP and VPN Detection

Two additional Vercel endpoints serve as reconnaissance modules. ip-check-notification-rkb[.]vercel[.]app/api and ip-check-notification-firebase[.]vercel[.]app/api return JSON responses containing the requesting IP address, VPN detection status, and a reassuring note: "Connection appears normal. For best performance, avoid VPNs or anonymized routes." These modules allow the operators to filter out sandboxes, VPN users, and security researchers before engaging a target with the actual malware payload.

DPRK / Lazarus Attribution

This campaign maps directly to the DPRK threat cluster tracked as Contagious Interview (CL-STA-0240). The overlap with our previous CUDA toolkit analysis and with documented Lazarus tradecraft is structural, not circumstantial:

Recruiting-themed lures targeting developers through fake job assessments are the defining characteristic of Contagious Interview, documented by Palo Alto Unit 42, Group-IB, and multiple government advisories. The use of Node.js new Function() for dynamic code execution mirrors the Python eval() and exec() patterns in BeaverTail/InvisibleFerret. Multiple redundant execution paths (VS Code, dev server, backend) reflect the same defense-in-depth-of-offense philosophy we observed in the CUDA toolkit\'s four persistence mechanisms.

The infrastructure choices align with known DPRK procurement patterns. Budget US VPS providers (Tier.Net at $3-5/month, Interserver) are consistent with DPRK operational infrastructure documented by Mandiant and Recorded Future. The use of Vercel\'s free tier for staging eliminates payment trails. The exceptionId values (env101383, env99*) suggest organized tracking of multiple targeting campaigns or environments, indicating this is not a one-off operation but part of a managed program.

Microsoft references prior DPRK campaigns using VS Code projects in their report. The repository naming conventions (Cryptan, JP-soccer, RoyalJapan, SettleMint) follow patterns identified in earlier Lazarus developer targeting operations. The monobyte/chaosbyte branding and Mailchimp email collection suggest an actor building long-term targeting infrastructure, not conducting opportunistic attacks.

IOC Summary

C2 Servers

IndicatorTypeNotes
147.124.202.208:3000C2 IPExpress.js, LIVE
163.245.194.216:3000C2 IPExpress.js, LIVE
87.236.177.9:3000C2 IPDOWN
66.235.168.136:3000C2 IPDOWN

Staging and Social Engineering Domains

IndicatorTypeNotes
api-web3-auth[.]vercel[.]appStagingServes cleartext Stage 1 loader
oracle-v1-beta[.]vercel[.]appStagingServes obfuscated Stage 1 loader
monobyte-code[.]vercel[.]appSocial EngineeringFake code editor landing page
ip-check-notification-rkb[.]vercel[.]appReconIP/VPN profiling
ip-check-notification-firebase[.]vercel[.]appReconIP/VPN profiling
price-oracle-v2[.]vercel[.]appStagingTaken down
coredeal2[.]vercel[.]appStagingTaken down
vscodesettingtask[.]vercel[.]appStagingTaken down
ip-checking-notification-kgm[.]vercel[.]appReconStatus unknown
ip-check-notification-03[.]vercel[.]appReconStatus unknown
ip-check-wh[.]vercel[.]appReconStatus unknown
ip-checking-notification-firebase111[.]vercel[.]appReconStatus unknown
ip-check-notification-firebase03[.]vercel[.]appReconStatus unknown

File Hashes (SHA-256)

HashDescription
c15111d9246e845b7d8dcffc6b9c351beb12aaebcf70c1ad980dcf8b1f1965b7Stage 1 loader (cleartext, acquired live)
ae87e37db08525a46be1024068e44521c8a233a23c8069e349f0c2bcfacf634cStage 1 loader (obfuscated, acquired live)
49bc956bd495526b1009172221d15dda75a860d523dbc1a91ebd194e2ddec96aStage 2 tasking client (obfuscated, captured live from C2)
2b3a5abf9aebe5cc5082fef65548c6670f9228b4cd1b682b966a223457ce2b49Stage 2 tasking client (deobfuscated)
ddd43e493cb333c1cc5d7cd50a6a5a61ecd89cfa5f4076f62c2adf96748b87f8Malicious repo artifact
449e2bf57ab4790427a3a7de3d98b6c540e76190a3d844de2f0e7b66be842b19Malicious repo artifact
07ad8525844ce61471e08e8c515b76bf063bac482394152bad814026cd577f69Malicious repo artifact
e4d71aa95be0725c351e9d1d273d35ccdb0a8bdb31a57927c8738431b89788f5Malicious repo artifact
13152dcb3be425e1ce0f085cd733121a4665cf9935cf8867738e3d510a80308aMalicious repo artifact
6d59740d0710da370d5c38ddf88d6912487a1799e4ad09b72d764a3d27ed16b3Malicious repo artifact

Network Indicators

IndicatorContext
GET /api/errorMessageStage 1 C2 beacon endpoint
POST /api/handleErrorsStage 2 tasking endpoint (captured live)
POST /api/reportErrorsStage 2 error telemetry endpoint
exceptionId=env101383Campaign/target identifier
chaosbyte.us9.list-manage.comMailchimp email harvesting
deps-theardStage 2 error category (attribution fingerprint)

Intercept Cell Research is a Cipher Cortex research program. Hooked Scams is a scam investigation series from Intercept Cell Research.