Skip to content

MALFEX npm Package imgdecode Hides Windows DLL Loader to Inject Shellcode Into dllhost.exe

MALFEX npm Package imgdecode Hides Windows DLL Loader to Inject Shellcode Into dllhost.exe
Category: Incident & Malware Analysis | Target Ecosystem: npm / Node.js on Windows | Published: October 17, 2026
Key Intelligence Summary

A malicious npm package named imgdecode (versions 1.0.0 through 1.0.3), published by user malfexteste8, remained live on the public npm registry after accumulating 979 downloads [1, 2]. Rather than relying on a noisy postinstall script or plain JavaScript, the package embeds a compiled Windows native Node.js addon (prebuilds/win32-x64/imgdecode.node) [1]. When imported via require('imgdecode') inside an interactive Windows console, the DLL fetches an AES-256-CBC encrypted payload from GitHub, decrypts it in memory, and injects shellcode into a newly spawned, suspended dllhost.exe process [1, 2].

Blocking postinstall scripts has become standard advice for engineering teams trying to harden Node.js environments against malicious dependencies. The threat actor behind the long-running MALFEX software supply chain campaign appears to have anticipated that control [1].

According to technical analysis published by open-source supply chain security firm SafeDep on October 13, 2026, and subsequent reporting by The Hacker News on October 17, 2026, the MALFEX operator pivoted from multi-package JavaScript loaders to a self-contained Windows dynamic-link library (DLL) packaged inside imgdecode [1, 2]. By the time secondary reports circulated on October 17, imgdecode had reached version 1.0.3, logged 979 downloads, and was still available for installation from npm [2].

Because the malicious logic executes inside the native addon's module initialization routine when a program calls require('imgdecode'), running npm install --ignore-scripts offers zero protection once the library is imported on an interactive Windows workstation [1].

Campaign Context: How MALFEX Pivoted From Split JS Chains to Native Binaries

MALFEX is not a newly minted operation. Security researchers at CloudSEK (on September 30, 2026) and Checkmarx (on October 5, 2026) first documented the campaign, linking an unknown threat actor—assessed by researchers to be a Portuguese-speaking operator—to a cluster of malicious npm packages dating back to August 6, 2023 [1, 2, 4]. Across five publisher accounts and 12 total packages (eight malicious and four benign decoys), the campaign amassed more than 40,000 combined downloads while delivering Windows payloads including the Overlord remote access trojan (RAT), downloaders, and an information stealer tracked as movinlike [1, 4].

What makes the late-September and October 2026 activity stand out is the speed of the operator's tactical adaptation. Earlier MALFEX packages used a split three-package architecture so that no single library looked overtly malicious on its own [1, 2]:

  • Fetch stage (cdn-img-fetch): Downloaded an encrypted binary blob hosted in a GitHub repository (cavecrew/proj) [1].
  • Decrypt stage (img-to-native): Decrypted the payload using the key malfexteam2027 and wrote a Go executable to disk at %APPDATA%\Microsoft\Windows\node_runtime_helper.exe [1, 2].
  • Execution stage (native-runner): Launched the dropped executable, which reached out to 104.234.65[.]75:700 to pull down the setup.exe (movinlike) stealer [2].

When npm security teams began removing components of that three-part chain on September 28, 2026, the attacker responded within hours [1, 2]. First, they rolled out replacement packages (imgbundle and runhelper) on September 30 [1, 2]. Then, just over six hours after runhelper was unpublished, the operator uploaded imgdecode 1.0.0—shifting the shellcode execution logic out of JavaScript foreign-function calls and directly into a compiled Windows native addon [1, 2].

MALFEX Architecture Evolution: Split JavaScript Chain vs. Consolidated Native DLL Illustrative comparison showing how the attacker replaced three separate npm packages (cdn-img-fetch, img-to-native, native-runner) and transitional packages (imgbundle, runhelper) with a single native Node.js addon package (imgdecode v1.0.3). PHASE 1: SPLIT JAVASCRIPT CHAIN (Sept 26 - Sept 28, 2026) — REMOVED BY NPM 1. cdn-img-fetch Fetches encrypted blob from github.com/cavecrew/proj 2. img-to-native Decrypts (key: malfexteam2027) & drops Go binary to disk 3. native-runner Executes Go binary to pull setup.exe (104.234.65[.]75) PHASE 2: CONSOLIDATED NATIVE ADDON (Sept 30 - Oct 11, 2026) — ACTIVE IN IMGDECODE 1.0.3 imgdecode (index.js) 9-line JS wrapper loads: prebuilds/win32-x64/ imgdecode.node (No scripts) Native Windows DLL (N-API Init Routine in C/C++) 1. Checks interactive console via GetConsoleMode (evades CI/sandboxes) 2. Downloads banner.jpg via WinINet & decrypts in-memory via bcrypt.dll 3. Spawns suspended dllhost.exe & injects shellcode via CreateRemoteThread
Figure 1 (Illustrative Architecture Diagram): Comparison of the earlier MALFEX three-package JavaScript chain versus the self-contained native C/C++ Node.js addon architecture introduced in imgdecode [1, 2].

Timeline of Takedowns and Operator Replacements

Tracking the publication timestamps across late September and October 2026 shows how closely the operator monitored package removals and public threat intelligence reports [1, 2]. Notably, version 1.0.3 of imgdecode was pushed on October 11, 2026—six days after Checkmarx published its detailed disclosure—specifically to sever the remaining dependency on imgbundle after npm removed imgbundle on October 7 [1, 2].

Date & Time (UTC) Package / Version Event & Technical Significance
Sept 26, 2026 cdn-img-fetch, img-to-native, native-runner (1.0.0) Initial publication of the three-part JavaScript stealer chain on npm [1, 2].
Sept 28, 2026 (16:06 UTC) img-to-native, native-runner Both packages are removed from the npm registry [2].
Sept 30, 2026 (03:29 UTC) imgbundle (1.0.0) Published by the operator to replace the removed img-to-native package [1, 2].
Sept 30, 2026 (03:37 UTC) runhelper (1.0.0) Published to replace native-runner; invokes VirtualAlloc, RtlMoveMemory, and CreateThread from JavaScript [1, 2].
Sept 30, 2026 (04:13 UTC) runhelper (1.0.0) Unpublished from npm less than 40 minutes after release [2].
Sept 30, 2026 (10:32 UTC) imgdecode (1.0.0) Uploaded by malfexteste8 ~6 hours after runhelper disappeared; introduces the compiled Windows DLL loader, initially waiting for imgbundle [1, 2].
Sept 30, 2026 (11:07 UTC) imgdecode (1.0.1) Moves the download, decryption, and injection routine directly into the N-API module initialization function [1, 2].
Oct 1, 2026 (17:48 UTC) imgdecode (1.0.2) Incremental update published by malfexteste8 [2].
Oct 5, 2026 Public Disclosure Checkmarx publishes its analysis of the broader 8-package MALFEX campaign (following CloudSEK's Sept 30 report); neither report lists imgdecode [1, 4].
Oct 7, 2026 (11:06 UTC) imgbundle Removed from npm by registry administrators [2].
Oct 8, 2026 (13:03 UTC) cdn-img-fetch Removed from npm [2].
Oct 11, 2026 (10:09 UTC) imgdecode (1.0.3) Removes the dependency on imgbundle, making imgdecode a fully standalone malicious loader [1, 2].
Oct 13 – Oct 17, 2026 imgdecode (1.0.3) SafeDep discloses the native DLL loader (Oct 13); The Hacker News reports the package remains live on npm with 979 downloads (Oct 17) [1, 2].

Inside imgdecode: How the Native Windows DLL Loader Works

On the surface, imgdecode presents itself as a lightweight image buffer decoding utility, complete with registry keywords like image, decode, buffer, native, and addon [1]. Its JavaScript entry point (index.js) is only nine lines long [1, 2]. All it does is load a precompiled binary located at prebuilds/win32-x64/imgdecode.node [1].

A `.node` file in Node.js is simply a compiled native shared object—on Windows, a standard PE dynamic-link library (DLL) [1]. Starting in version 1.0.1, the operator placed the malicious loader trigger inside the N-API module initialization function [1]. As soon as a developer's application executes require('imgdecode'), Windows maps the DLL into `node.exe` and runs the initialization callback automatically [1].

1. The Registry Metadata Anomaly

SafeDep highlighted an unusual discrepancy in how imgdecode is presented on npm versus what actually lands on disk [1]. The saved npm registry metadata lists an install script: install: node-gyp rebuild [1]. However, the actual package.json file inside the downloaded archive omits that script entirely, and the tarball contains no binding.gyp file or C/C++ source code [1].

While SafeDep noted it has not confirmed how npm's backend created or handles this metadata mismatch during installation, the operational impact for defenders is clear: the package does not need a build step or an install script [1]. Because the compiled DLL is already bundled inside the prebuilds/ folder, security policies that rely solely on blocking lifecycle scripts do not stop execution [1].

2. Interactive Console Check (CI and Sandbox Evasion)

Automated package scanners and CI/CD pipelines frequently run packages inside headless containers or virtual machines where standard output (`stdout`) is redirected to a log file or pipe. To avoid triggering alerts in those environments, `imgdecode.node` immediately checks whether it is attached to an interactive Windows console [1].

Specifically, the native routine retrieves the standard output handle and calls the Windows API function GetConsoleMode [1]. If standard output is redirected—as is typical in automated sandboxes and CI jobs—the check fails and the malicious loader exits silently without making any network requests [1].

3. XOR String Obfuscation and Dynamic API Resolution

To hinder static string extraction by YARA rules or basic binary scanners, the DLL obfuscates its sensitive strings with a single-byte XOR key of 0x37 [1]. Longer strings—such as the target GitHub URL and decryption passphrase—are stored as 16-byte aligned blocks inside the binary's read-only data (.rdata) section and decoded at runtime using SSE xorps instructions against a 16-byte register filled with 0x37 [1]. Shorter strings are constructed directly on the stack and decoded in a tight loop [1].

Similarly, while the PE import table visibly references WinINet, bcrypt.dll, and KERNEL32.dll, the specific Windows APIs responsible for process hollowing and thread injection are resolved dynamically at runtime via GetProcAddress [1].

4. Payload Retrieval, AES-256-CBC Decryption, and the Image Kill-Switch

Once the console check passes and the strings are decoded in memory, the DLL initiates an HTTPS request using the Windows Internet (`WinINet`) API with a hardcoded Mozilla/5.0 User-Agent header [1]. It fetches the encrypted payload from the following GitHub URL [1]:

hxxps://raw.githubusercontent[.]com/cavecrew/proj/main/banner.jpg

Despite the `.jpg` file extension, the downloaded file is an encrypted binary blob kept entirely in memory [1]. The DLL uses the Windows Cryptography API: Next Generation (CNG, via bcrypt.dll) to decrypt the response using AES-256 in Cipher Block Chaining (CBC) mode [1]:

  • Key Derivation: The 256-bit AES key is generated by computing the SHA-256 hash of the ASCII passphrase nif-runtime-2027 [1].
  • Initialization Vector (IV): The first 16 bytes of the downloaded HTTP response serve as the CBC initialization vector, while the remaining bytes make up the ciphertext [1].

After decryption completes, the loader performs a clever validation check on the first bytes of the resulting plaintext [1]. If the decrypted bytes match the magic header signature of a genuine JPEG or PNG image, the DLL immediately stops execution [1]. SafeDep assesses (as an analytical inference) that this serves as a server-side kill switch: the operator can disarm active loaders in the wild simply by replacing `banner.jpg` on GitHub with an AES-encrypted version of a real image, whereas replacing it with an unencrypted image would cause the CNG decryption call to fail beforehand [1].

5. Injection Into dllhost.exe and Local Thread Fallback

If the decrypted payload is not a JPEG or PNG image, the loader treats the plaintext as raw shellcode and prepares to inject it into a legitimate Windows host binary [1].

imgdecode.node Runtime Execution and Process Injection Flow Diagram showing the 5 runtime stages of imgdecode.node: console verification via GetConsoleMode, payload fetch from GitHub via WinINet, AES-256-CBC decryption via bcrypt.dll, JPEG/PNG header check, and VirtualAllocEx/WriteProcessMemory/CreateRemoteThread injection into suspended dllhost.exe. STEP 1: INITIALIZATION & CHECK require('imgdecode') Calls GetStdHandle & GetConsoleMode() STEP 2: XOR & WININET FETCH Decodes strings (XOR 0x37) GET /cavecrew/proj/... .../main/banner.jpg STEP 3: CNG DECRYPTION AES-256-CBC (bcrypt.dll) Key: SHA256("nif-runtime -2027") | IV: First 16B STEP 4: MAGIC BYTE KILL-SWITCH CHECK Checks plaintext header: If JPEG or PNG signature matches, abort. Otherwise, proceed with raw shellcode injection. STEP 5A: PRIMARY TARGET — SUSPENDED DLLHOST.EXE Spawns %SystemRoot%\System32\dllhost.exe (Suspended, No Window) VirtualAllocEx -> WriteProcessMemory -> VirtualProtectEx -> CreateRemoteThread STEP 5B: FALLBACK INJECTION If remote injection fails, runs inside node.exe via CreateThread
Figure 2 (Illustrative Process Flow): Execution sequence inside imgdecode.node (versions 1.0.1–1.0.3) showing how the loader verifies an interactive console, decrypts banner.jpg in memory, and injects shellcode into dllhost.exe [1].

The DLL spawns a new instance of %SystemRoot%\System32\dllhost.exe in a suspended state with no visible window and no console [1]. It then executes a classic four-step remote process injection sequence [1]:

  1. Calls VirtualAllocEx to allocate a read-write memory region inside the suspended dllhost.exe process [1].
  2. Calls WriteProcessMemory to copy the decrypted shellcode bytes into that buffer, verifying that the full byte count was written [1].
  3. Calls VirtualProtectEx to change the memory region's permissions for execution (notably, SafeDep observed that the injector does not check the return value of VirtualProtectEx before proceeding) [1].
  4. Calls CreateRemoteThread to start execution of the injected shellcode while leaving the original main thread of dllhost.exe suspended [1].

And if the remote process injection routine reports a failure? The developer built in a fallback mechanism. Rather than giving up, the DLL allocates memory directly inside the current node.exe process using VirtualAlloc, copies the payload via RtlMoveMemory, adjusts permissions with VirtualProtect, and launches the shellcode locally via CreateThread [1].

Downstream Stages and Honest Uncertainty

Analytical Boundary: What Remains Unconfirmed

SafeDep explicitly states that the exact next stage delivered by the native imgdecode loader from banner.jpg remains unconfirmed [1]. However, because imgdecode shares the same GitHub repository (cavecrew/proj) and encryption key patterns used in earlier MALFEX packages, SafeDep infers that banner.jpg likely delivers the same Stage 2 Go downloader and Stage 3 stealer/RAT observed in the broader campaign [1].

In the confirmed stages recovered by SafeDep from the closely linked MALFEX delivery chain, execution proceeds through two additional components [1, 2, 3]:

  • Stage 2 (Go 1.27.1 Downloader): A compiled Go binary with the internal module name downloader (written to disk in earlier variants as %APPDATA%\Microsoft\Windows\node_runtime_helper.exe, with one recovered sample matching SHA-256 prefix 2f268ca7… previously documented by Checkmarx) [1, 2]. This binary reaches out to 104.234.65[.]75:700 to download the third-stage payload, saving it as %TEMP%\svc_update.exe or setup.exe [1, 2].
  • Stage 3 (movinlike Stealer & Remote Access Tool): Before executing its primary harvesting routines, the Stage 3 payload runs anti-analysis checks for virtual machines, sandbox usernames, and debugging or monitoring tools including x64dbg, Process Monitor (procmon), and Wireshark [1, 3]. Once active, it hunts for cryptocurrency wallet data, seed phrases, browser credentials, Discord and Telegram tokens, gaming application data, and sensitive local files matching extensions such as .env, .pem, .ppk, and .kdbx [2, 3].

Beyond passive credential theft, the Stage 3 malware establishes a persistent WebSocket command-and-control (C2) channel to 104.234.65[.]75:7007 supporting remote shell execution, process control, and arbitrary file transfer [3]. To survive reboots on compromised Windows endpoints, it creates a Registry Run key alongside a scheduled task disguised as MicrosoftEdgeUpdateService (with related artifacts under %APPDATA%\Microsoft\EdgeUpdate) [1, 3]. Earlier branches of the MALFEX campaign also created a scheduled task named \Maiden and dropped an AutoIt3.exe binary inside %LOCALAPPDATA%\ScopeSmart Technologies Inc [4].

Detection Engineering and Behavioral Hunting

Because imgdecode executes via a prebuilt native addon during normal module import, software composition analysis (SCA) tools that only inspect `package.json` for suspicious `postinstall` commands—or static scanners that only parse `.js` files—will miss the loader entirely [1]. Defenders should focus on three high-signal detection layers recommended by SafeDep [1]:

  1. Process Lineage Anomalies: Monitor Windows endpoint telemetry (such as Sysmon Event ID 1 or EDR process creation logs) for node.exe spawning dllhost.exe as a child process—especially when dllhost.exe is launched with no command-line arguments (legitimate COM surrogate instances normally include a /Processid:{GUID} argument) [1].
  2. Suspicious Native Addon Imports: Scan `.node` files inside `node_modules` for anomalous PE import tables. A legitimate image-decoding addon has no reason to import both Windows networking libraries (WinINet or WinHTTP) and cryptographic APIs (bcrypt.dll) [1].
  3. Orphaned Prebuilt Binaries: Flag npm packages that ship compiled DLLs inside a prebuilds/ directory while lacking corresponding C/C++ source files or a binding.gyp build configuration in the package archive [1].

Indicators of Compromise (IOCs)

Engineering and security operations teams should audit their projects, developer workstations, and Windows-based build agents against the following verified indicators from SafeDep, The Hacker News, Checkmarx, and CloudSEK [1, 2, 3, 4]:

Indicator Type Value / Artifact Context & Status
npm Package (Active Loader) imgdecode (versions 1.0.0, 1.0.1, 1.0.2, 1.0.3) Published by malfexteste8; contains native Windows DLL loader (979 downloads) [1, 2].
npm Packages (Removed Chain) imgbundle (1.0.0–1.0.2), runhelper (1.0.0), cdn-img-fetch, img-to-native, native-runner Earlier and transitional MALFEX JavaScript loader packages removed by npm [1, 2].
npm Packages (Broader MALFEX) function-flag, function-color Downloader packages tied to the broader 8-package MALFEX campaign [4].
Malicious Native Binary Path prebuilds/win32-x64/imgdecode.node Compiled Windows DLL loaded by index.js on require('imgdecode') [1].
Payload URL hxxps://raw.githubusercontent[.]com/cavecrew/proj/main/banner.jpg AES-256-CBC encrypted shellcode fetched via WinINet with Mozilla/5.0 User-Agent [1].
Cryptographic Passphrases nif-runtime-2027 (SHA-256 hashed for AES-256-CBC)
malfexteam2027 (used in img-to-native)
Decryption keys hardcoded into the native DLL (XOR 0x37) and JS decryptor [1, 2].
C2 & Staging Servers 104.234.65[.]75:700 (Stage 3 download)
104.234.65[.]75:7007 (WebSocket C2)
Used by the Stage 2 Go downloader and Stage 3 stealer/RAT [2, 3].
Host File System Artifacts %TEMP%\svc_update.exe
%APPDATA%\Microsoft\Windows\node_runtime_helper.exe
%APPDATA%\Microsoft\EdgeUpdate
%LOCALAPPDATA%\ScopeSmart Technologies Inc\AutoIt3.exe
Dropped executables and persistence directories across MALFEX stages [1, 2, 4].
Scheduled Tasks MicrosoftEdgeUpdateService
\Maiden
Windows persistence tasks created by MALFEX payloads [3, 4].
Stage 2 Downloader SHA-256 2f268ca7… (prefix confirmed in public reporting) Go 1.27.1 downloader binary recovered by SafeDep and matched to Checkmarx IOCs [1].

Incident Response and Remediation Guidance

Simply running npm uninstall imgdecode does not remediate an infected workstation [1, 2]. If a developer imported imgdecode inside an interactive terminal on Windows, the payload has likely already injected into dllhost.exe or established persistence on the host [1, 3].

  • Isolate Affected Windows Machines: Any Windows workstation where imgdecode (or any of the associated MALFEX packages) was installed and imported should be treated as compromised and isolated from corporate networks immediately [1, 2].
  • Rotate All Exposed Secrets and Seed Phrases: Because the Stage 3 stealer specifically targets .env files, SSH/TLS private keys (.pem, .ppk), KeePass databases (.kdbx), browser session tokens, and cryptocurrency wallets, developers must revoke and reissue all local API keys, cloud credentials, npm/GitHub tokens, and transfer cryptocurrency funds to freshly generated hardware-backed wallets [2, 3].
  • Hunt for Persistence and Orphaned Processes: Check Windows Task Scheduler for MicrosoftEdgeUpdateService and \Maiden, inspect Registry Run keys, terminate unauthorized dllhost.exe processes spawned without /Processid: flags, and reimage confirmed compromised hosts [1, 3, 4].

Sources & References

  1. [1] SafeDep (Primary Technical Disclosure): "MALFEX Moves Its npm Loader into a Native Windows DLL," published October 13, 2026. https://safedep.io/malfex-imgdecode-npm-native-addon-loader/
  2. [2] The Hacker News: "MALFEX npm Package 'imgdecode' Hides Shellcode Loader in Windows Native Addon," published October 17, 2026. https://thehackernews.com/2026/10/malfex-npm-package-imgdecode-hides.html
  3. [3] Quasar CyberTech (QPulse Threat Brief #06967): "MALFEX Campaign: 'imgdecode' npm Package Deploys Shellcode Loader via Windows Native Addon," published October 17, 2026. https://qpulse.quasarcybertech.com/news/6967/malfex-campaign-imgdecode-npm-package-deploys-shellcode-loader-via-windows-native-addon
  4. [4] AppAuxin (Summary of Checkmarx & CloudSEK MALFEX Reports): "MALFEX npm Malware: 8 Packages to Check and Remove," published October 7, 2026. https://appauxin.com/malfex-npm-malware-packages-check/
NK

Cybersecurity Researcher & Technical Editor

UnpanicTech publishes independent cybersecurity research and defensive security analysis. Naseem Khan serves as Technical Editor, with a focus on vulnerability analysis, defensive security, incident response, cloud security, and practical security engineering.