Defeating Node.js Malware through API Tracing

Sven Rath (Security Researcher · Check Point Research)

FIRSTCON26 — 38th Annual FIRST Conference

Overview

In an insightful talk at FIRSTCON, Sven Rath, a Security Researcher at Check Point Research, delved into the escalating threat of Node.js malware and introduced a powerful dynamic analysis technique: API tracing. Titled "Defeating Node.js Malware through API Tracing," Rath's presentation highlighted the unique challenges posed by malware written in Node.js, including its heavy obfuscation and ability to bypass traditional static antivirus detections. He then unveiled his open-source tool, the Node.js tracer, demonstrating its efficacy in cutting through these defenses.

Watch on YouTube

Visual summary for Defeating Node.js Malware through API Tracing by Sven Rath
Visual summary for Defeating Node.js Malware through API Tracing by Sven Rath

Key moments

  1. 0:00 Introduction and common Node.js malware examples
  2. 2:00 Understanding tracing: dynamic analysis benefits and limits
  3. 6:30 Attacker's view: Node.js malware pros and cons
  4. 10:00 Crash course: typical Node.js malware execution flow
  5. 14:00 Tracer mechanics: hooking Node.js APIs and bypassing anti-analysis
  6. 18:40 Live demo: uncovering malware behavior with the tracer
  7. 22:00 Automating trace analysis using Large Language Models
  8. 28:00 Q&A: tracer benefits and potential bypasses

Defeating Node.js Malware through API Tracing

Speakers: Sven Rath, Security Researcher, Check Point Research

Conference: FIRSTCON

YouTube: https://www.youtube.com/watch?v=biV2jZL1ByA

Overview

In an insightful talk at FIRSTCON, Sven Rath, a Security Researcher at Check Point Research, delved into the escalating threat of Node.js malware and introduced a powerful dynamic analysis technique: API tracing. Titled "Defeating Node.js Malware through API Tracing," Rath's presentation highlighted the unique challenges posed by malware written in Node.js, including its heavy obfuscation and ability to bypass traditional static antivirus detections. He then unveiled his open-source tool, the Node.js tracer, demonstrating its efficacy in cutting through these defenses.

The talk articulated why Node.js has become a platform of choice for threat actors, ranging from sophisticated APT groups (like those behind the Contagious Interview campaign and BeaverTail malware) to cybercrime operations (such as Interlock Red and Node Snake) and the increasingly prevalent NPM supply chain attacks. Rath emphasized that despite the platform's advantages for malware authors, its runtime behavior cannot be hidden. His Node.js tracer leverages this principle, providing a method to understand malicious activity, defeat anti-analysis techniques, and even automate the triage process with the assistance of Large Language Models (LLMs). This approach is particularly critical for security professionals grappling with the rapidly evolving landscape of script-based threats.

The significance of Rath's work lies in providing a practical and efficient solution for analyzing a notoriously difficult class of malware. By focusing on the executed API calls, the tracer bypasses layers of obfuscation, revealing the true intent of the malicious code. Furthermore, its ability to spoof anti-analysis checks ensures that malware executes its full malicious payload, offering defenders a complete picture of its capabilities. The integration with LLMs represents a significant step forward in automating malware analysis workflows, enabling rapid extraction of Indicators of Compromise (IOCs) and behavioral insights, which are invaluable for Security Operations Center (SOC) environments.

Background

▶ Watch: Introduction and common Node.js malware examples (0:00)

The proliferation of JavaScript-based applications, particularly with the advent of Node.js, has inadvertently opened new avenues for malicious actors. Node.js, a JavaScript runtime built on Chrome's V8 engine, allows JavaScript to be executed outside of a web browser, making it suitable for server-side development and desktop applications (via frameworks like Electron). This versatility, however, has also attracted threat actors for several compelling reasons.

From a malware author's perspective, Node.js offers significant advantages. Firstly, JavaScript is inherently easy to obfuscate, and numerous tools exist (e.g., obfuscator.io) that can transform readable code into an "atrocious blob" of unreadable syntax in seconds. This heavy obfuscation makes static analysis by antivirus engines extremely challenging, as benign and malicious obfuscated code can appear identical. Consequently, Node.js malware often exhibits very low detection rates on platforms like VirusTotal. Secondly, for distribution, Node.js malware can be bundled with its runtime (using packers like Nexe or Electron), creating a standalone executable that runs on victim machines even if Node.js is not pre-installed. This "install-free" nature simplifies deployment. Lastly, the ease of writing JavaScript, especially in an era of LLMs, makes development accessible, potentially lowering the barrier to entry for new crimeware developers.

However, Node.js malware also presents certain drawbacks for threat actors. Bundling the runtime significantly increases executable size, often leading to large binaries that are typically distributed through pretexts like "cracked software" or "malvertising," where users expect large downloads. More importantly, JavaScript's direct interaction with the operating system is limited; it must rely on the Node.js API. This constraint often forces malware to resort to calling external processes (e.g., PowerShell) or dropping and executing subsequent stages to achieve full functionality, which can create additional, detectable Indicators of Compromise (IOCs). This often positions Node.js malware as a first-stage loader or dropper.

The talk also touched upon interesting features and techniques relevant to Node.js in a security context. Node Add-ons are DLLs that can be called from Node.js, allowing native code execution and bypassing the limited Node.js API for more direct OS interaction. While less common in malware, V8 bytecode (compiled JavaScript) presents a different analysis challenge, though tools like Check Point's View 8 decompiler exist. A particularly sneaky technique is Electron backdooring or hijacking, where threat actors modify the JavaScript files of popular Electron-based applications (like Microsoft Teams or Discord) stored on disk. If these applications lack integrity checks, the malware achieves persistence by being executed every time the legitimate application starts, offering a stealthy way to maintain control.

Given these dynamics, traditional static analysis often fails against Node.js malware. This necessitates robust dynamic analysis methods that can observe the malware's behavior at runtime, cutting through the layers of obfuscation and anti-analysis to reveal its true malicious intent.

Key Findings

▶ Watch: Attacker's view: Node.js malware pros and cons (6:30)

Sven Rath's research and the development of the Node.js tracer led to several key findings regarding the analysis and defeat of Node.js malware:

  • Obfuscation is the Primary Defense: Node.js malware heavily relies on JavaScript obfuscation, rendering static analysis largely ineffective. Antivirus engines struggle to differentiate malicious code from legitimate, obfuscated intellectual property, leading to low detection rates.
  • Runtime Behavior Cannot Be Hidden: Despite extensive obfuscation, malware must interact with the operating system or Node.js runtime APIs to perform any actions. This fundamental principle makes dynamic analysis, specifically API tracing, a powerful countermeasure.
  • Node.js APIs are Trivial to Hook: Unlike complex native binaries, JavaScript's script-based nature makes hooking function calls remarkably simple, often requiring just a few lines of code to override and instrument an API.
  • Universal Module Hooking is Achievable: By targeting Node.js's internal load API, the tracer can programmatically intercept and hook every function within every module as it's loaded by the malware. This provides comprehensive visibility into its execution.
  • Anti-Analysis Checks are Defeatable Systematically: Node.js malware frequently employs anti-analysis checks (e.g., os.totalmem() for RAM, os.cpus() for CPU cores, child_process.exec() for process lists like tasklist, or PowerShell commands for hardware vendor checks). These can be consistently bypassed by spoofing the return values of the hooked APIs, ensuring the malware executes its full malicious payload.
  • Injection via --require / NODE_OPTIONS is Powerful: The Node.js --require command-line flag or the NODE_OPTIONS environment variable allows a script to be preloaded before the main application. This mechanism enables the tracer to inject its hooks even into packed Node.js executables (e.g., created by Nexe or Electron) without requiring prior unpacking.
  • Trace Logs are LLM-Friendly: Crucially, the detailed, chronological log of API calls generated by the tracer is highly structured and semantically rich, making it an ideal input for Large Language Models (LLMs) like Claude. This enables automated analysis, rapid extraction of IOCs (C2 servers, file paths, commands), and comprehensive behavioral summaries, significantly accelerating the triage process.
  • Scalable Dynamic Payload Extraction: The tracer can be configured to automatically save any files written to disk by the malware. This feature, combined with its anti-analysis bypass capabilities, allows for the large-scale extraction of subsequent stage payloads from numerous samples, even if the malware attempts to immediately delete them after execution.

These findings collectively demonstrate that while Node.js malware poses significant challenges to traditional security tools, a well-designed API tracing framework can effectively analyze, bypass, and automate the understanding of these sophisticated threats.

Technical Deep Dive

▶ Watch: Tracer mechanics: hooking Node.js APIs and bypassing anti-analysis (14:00)

The core of Sven Rath's solution lies in the elegant simplicity of API hooking within the JavaScript runtime environment. Unlike native binaries where hooking might involve complex memory manipulation or trampoline functions, JavaScript's dynamic nature makes it straightforward. As Rath demonstrated, hooking a function like fs.open requires merely five lines of code: saving the original function reference, replacing the function with a new one that performs logging/spoofing, and then calling the original function to maintain legitimate execution flow.

However, hooking individual APIs one by one would be impractical for comprehensive malware analysis. To achieve universal instrumentation, Rath's Node.js tracer targets a more fundamental internal Node.js API: the load function. This function is invoked every time a new module or library is loaded by the Node.js runtime. By intercepting this load call, the tracer can programmatically iterate through all the functions exposed by the newly loaded module and apply hooks to each of them. This ensures that virtually all functionality utilized by the malware, as it loads its various components, becomes subject to tracing.

A critical aspect of the tracer's effectiveness is its ability to defeat anti-analysis checks commonly found in Node.js malware. These checks are designed to detect execution in virtual machines, sandboxes, or analyst environments. Rath illustrated several examples:

  • Memory and CPU Checks: Malware often queries system resources using os.totalmem() (total memory) and os.cpus() (CPU count). If these return values indicative of a low-resource VM (e.g., less than 4GB RAM, fewer than two CPU cores), the malware exits. The tracer spoofs os.totalmem() to consistently return a high value like 16GB and os.cpus() to return an array representing multiple powerful cores (e.g., four i7 cores), effectively bypassing these checks.
  • Process Enumeration: Some malware executes external commands like tasklist via child_process.exec() to check for the presence of analysis tools (e.g., Wireshark, IDA Pro). The tracer hooks child_process.exec() and returns an empty string or a sanitized output for these specific commands, preventing the malware from detecting the analyst's tools.
  • PowerShell-based System Checks: Due to Node.js's limited direct OS interaction, malware frequently calls PowerShell to gather system information, such as the number of physical ports or the graphics card vendor. If the results suggest a virtualized environment (e.g., zero physical ports, VMware graphics card vendor), the malware exits. The tracer intercepts these PowerShell calls and returns spoofed values (e.g., "40,000" for physical ports, a non-VM vendor for the GPU), ensuring continued execution.

The ingenious method for injecting these hooks into the malware is through Node.js's --require command-line flag or the NODE_OPTIONS environment variable. This feature allows a specified script to be preloaded and executed before the main application script. By setting NODE_OPTIONS="--require /path/to/tracer.js", the tracer script runs first, establishes all its hooks, and then the malware runs with an instrumented environment. Crucially, this works even for packed Node.js executables (like those created with Nexe or Electron), as these packers respect the NODE_OPTIONS environment variable. This eliminates the need for complex unpacking procedures, simplifying the analysis workflow significantly.

Beyond core tracing and anti-analysis, Rath's tool incorporates several quality-of-life features for reverse engineers:

  • Skipping Sleep Calls: Malware often includes long sleep delays to evade sandboxes. The tracer replaces setTimeout and setInterval with functions that return immediately, speeding up analysis.
  • Saving Written Files: Any files dropped or created by the malware are automatically saved to a designated directory, which is invaluable for extracting subsequent stage payloads.
  • Stack Traces: Displaying stack traces alongside API calls can assist in understanding the context of the call and correlating it with static analysis efforts.
  • HTTP Request Logging: Detailed logging of HTTP/HTTPS requests, including headers and body, helps identify C2 communication and data exfiltration.

This comprehensive technical approach enables the Node.js tracer to provide unparalleled visibility into the behavior of highly obfuscated Node.js malware, even when packed and armed with anti-analysis mechanisms.

Demo / Proof of Concept

▶ Watch: Live demo: uncovering malware behavior with the tracer (18:40)

Sven Rath vividly demonstrated the Node.js tracer's capabilities through two compelling proof-of-concept scenarios, showcasing both manual analysis and automated LLM-driven triage.

Demonstration 1: Manual Analysis of the Gootkit Loader

The target for the manual demonstration was a heavily obfuscated Node.js loader dubbed "Gootkit loader." This malware was part of a widespread campaign leveraging compromised YouTube accounts to distribute fake video game cheats and cracked software, ultimately dropping information stealers like Ramnit. The challenge was that this stage-zero loader refused to run in a typical virtual machine environment.

Rath began by executing the obfuscated Gootkit loader script on his analysis VM, with the Node.js tracer preloaded via the --require flag. After a mere two seconds, the tracer generated a detailed log of all API calls. Scrolling through the trace log, Rath highlighted several key observations:

  1. Module Loading Insights: Early require calls revealed the malware's intent. For instance, require('http') indicated network communication, and require('child_process') suggested the spawning of external processes.
  2. Anti-Analysis Bypass: The log explicitly showed calls to os.totalmem() and os.cpus(). Because the tracer had spoofed these functions, the malware continued its execution, believing it was running on a legitimate, high-resource machine. Further checks for blacklisted usernames/hostnames and the execution of tasklist were also bypassed, as the tracer returned sanitized or empty results.
  3. Malicious Behavior Unveiled: Once anti-analysis checks were satisfied, the trace log clearly revealed the malware's true actions:
  • File System Interaction: Calls to file system APIs showed the creation of a log file, which was later used as a mutex.
  • Anti-Security Actions: The malware executed commands to kill Windows Defender, demonstrating its intent to disable endpoint security.
  • System Enumeration: PowerShell commands were executed to gather system information, such as the operating system version and active antivirus processes.
  • C2 Communication: Crucially, the tracer logged HTTP/HTTPS requests, revealing the first Command and Control (C2) server (https://example.com/log) where the enumerated system information was exfiltrated. A subsequent request identified the C2 endpoint for fetching the next stage payload (https://another-example.com/payload).

Within seconds, without any deobfuscation or static analysis, the tracer provided a comprehensive understanding of the malware's behavior and identified critical IOCs.

Demonstration 2: Automated Analysis with an LLM (Claude)

Building on the manual analysis, Rath then showcased the power of integrating the tracer with LLMs. He fed the raw trace log from the Gootkit loader into Claude, using a simple prompt: "You are a Node.js malware analyst who is given the task to analyze the given malware trace, figure out what this program is doing, For potential anti-analysis checks, mutexes, configurations, C2 server addresses, or relevant unpacking logic. Summarize your findings briefly in a markdown report."

The LLM's response was remarkably accurate and detailed:

  1. Overview and Classification: Claude correctly identified the program as a "Node.js based infostealer/dropper."
  2. Execution Flow Mapping: It meticulously mapped out the entire execution flow, noting reconnaissance and fingerprinting (OS version, AV products, running processes), accurate interpretation of VM/sandbox detection, and anti-security measures (killing AV via taskkill, creating a mutex file).
  3. C2 Infrastructure Extraction: The LLM precisely extracted all relevant C2 infrastructure, including the two C2 domains and their respective IPs, ports, and roles (primary C2 for exfiltration, secondary for next-stage payload).
  4. Campaign Identifier: It even identified a unique HTTP header, "Gotcha Family," as a campaign identifier, which was the source of the malware's internal naming.
  5. Formatted IOCs: The report concluded with a neatly formatted markdown table of all extracted IOCs.

This LLM integration demonstrated a profound capability for automating malware triage. Rath suggested workflows where security platforms (e.g., MWDB) could deploy Node.js malware in a container under the tracer, send the log to an LLM, and receive a ready-to-use markdown report, drastically reducing analysis time and expertise requirements for SOC analysts.

Finally, Rath mentioned a use case for large-scale dynamic payload extraction. When dealing with campaigns like GachiLoader with hundreds of samples, the tracer's ability to bypass anti-analysis and automatically save dropped files allowed him to run 300 instances in parallel. Within five minutes, he had all the C2 payloads and next-stage malware neatly saved, enabling comprehensive analysis of the campaign's scope and deployed payloads without manual intervention.

Defensive Implications

▶ Watch: Q&A: tracer benefits and potential bypasses (28:00)

The insights gained from analyzing Node.js malware with API tracing have several critical implications for defenders:

  1. Prioritize Dynamic Analysis: Static analysis alone is insufficient for Node.js malware due to its heavy obfuscation and the difficulty of distinguishing malicious code from legitimate, obfuscated intellectual property. Defenders must invest in robust dynamic analysis capabilities that can execute and observe Node.js binaries.
  2. Implement Node.js-Specific Tracing: Traditional system-level tracers (like strace or API Monitor for Windows PEs) may miss crucial internal Node.js API calls, particularly those related to anti-analysis or internal logic that doesn't directly touch the OS kernel. Specialized Node.js API tracers, like Sven Rath's tool, are essential for comprehensive visibility.
  3. Monitor for Anti-Analysis Techniques: Defenders should be aware of common Node.js anti-analysis checks (e.g., os.totalmem(), os.cpus(), child_process.exec('tasklist'), PowerShell queries for hardware details). Endpoint Detection and Response (EDR) solutions should be configured to flag or alert on such calls, especially when originating from Node.js processes.
  4. Guard Against NODE_OPTIONS Abuse: The --require flag and NODE_OPTIONS environment variable are powerful features that can be abused for injection. EDRs should monitor for unusual modifications to NODE_OPTIONS or the execution of Node.js processes with suspicious --require arguments, as this could indicate an attempt to inject malicious or analysis-evading code.
  5. Enhance Sandbox Evasion Defenses: Malware often incorporates sleep delays and checks for common sandbox indicators (e.g., low memory/CPU, specific usernames/hostnames). Dynamic analysis environments should include mechanisms to fast-forward sleep calls and spoof system information to ensure malware executes its full payload.
  6. Focus on Behavioral Indicators: Since static signatures are weak, defenders should focus on behavioral IOCs extracted from dynamic analysis. These include C2 server addresses, dropped file names/hashes, spawned process commands (especially PowerShell), and network communication patterns.
  7. Leverage LLMs for Triage: The demonstrated ability of LLMs to rapidly analyze trace logs and extract IOCs offers a significant opportunity for automation in SOC workflows. Integrating LLM-powered analysis into malware triage pipelines can dramatically reduce response times and the burden on human analysts.
  8. Hunt for Electron Backdooring: Organizations using Electron-based applications (e.g., Teams, Discord, Slack) should implement integrity checks for their JavaScript application files and monitor for unauthorized modifications to these files, as Electron backdooring is a viable persistence mechanism.
  9. Network Monitoring for C2 and Exfiltration: Even when obfuscated, Node.js malware still communicates over the network. Monitoring network traffic for unusual connections, C2 patterns, and data exfiltration from Node.js processes is crucial. The "Gotcha Family" HTTP header identified in the demo is an example of a specific network IOC that can be hunted.

By adopting these defensive strategies, organizations can significantly improve their ability to detect, analyze, and respond to the growing threat of Node.js malware.

Key Takeaways

  • Node.js malware is notoriously difficult to analyze statically due to heavy obfuscation, making dynamic API tracing an indispensable analysis method.
  • API hooking in Node.js is remarkably simple, and by targeting the internal load API, a tracer can universally instrument all modules and functions used by the malware.
  • Systematic spoofing of anti-analysis checks (e.g., memory, CPU count, process lists, hardware vendor checks) is highly effective, ensuring malware executes its full malicious payload in analysis environments.
  • The --require flag and NODE_OPTIONS environment variable provide a powerful and convenient injection vector for tracers, enabling analysis even of packed Node.js executables without prior unpacking.
  • Trace logs are ideal input for Large Language Models (LLMs), facilitating automated, rapid malware triage, comprehensive behavioral summaries, and precise IOC extraction, significantly enhancing SOC workflows.
  • This tracing approach enables dynamic payload extraction at scale, allowing security researchers to efficiently collect and analyze subsequent stage malware from widespread campaigns.

About the Speaker(s)

Sven Rath is a Security Researcher at Check Point Research, where his primary focus lies in the realm of crimeware and the broader cybercrime ecosystem. Based in Vienna, Sven is actively involved in the security community, frequently sharing his research and insights. He maintains a personal blog, everisince.com, where he discusses various topics related to malware. He is also reachable on X (formerly Twitter) and LinkedIn under his handle, everisince.

Reviews

Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT

This is a clean, well-executed tooling talk that actually solves a real analyst problem. Rath delivers a working, open-source Node.js tracer that cuts through obfuscation, defeats anti-analysis, and plugs straight into LLM-assisted triage. Not groundbreaking research, but genuinely useful work that will save people time.

Heather Calloway (CISO) — STRONG ACCEPT

Practical tooling for a real operational gap. Node.js malware is proliferating, static analysis is largely useless against it, and most SOCs don't have a playbook. This gives them one — plus an automation angle that actually works.

→ Top-rated talks at FIRSTCON26 — 38th Annual FIRST Conference

All talks from FIRSTCON26 — 38th Annual FIRST Conference