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 keymalfexteam2027and 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 to104.234.65[.]75:700to pull down thesetup.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].
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 (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]:
- Calls
VirtualAllocExto allocate a read-write memory region inside the suspendeddllhost.exeprocess [1]. - Calls
WriteProcessMemoryto copy the decrypted shellcode bytes into that buffer, verifying that the full byte count was written [1]. - Calls
VirtualProtectExto change the memory region's permissions for execution (notably, SafeDep observed that the injector does not check the return value ofVirtualProtectExbefore proceeding) [1]. - Calls
CreateRemoteThreadto start execution of the injected shellcode while leaving the original main thread ofdllhost.exesuspended [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
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 prefix2f268ca7…previously documented by Checkmarx) [1, 2]. This binary reaches out to104.234.65[.]75:700to download the third-stage payload, saving it as%TEMP%\svc_update.exeorsetup.exe[1, 2]. - Stage 3 (
movinlikeStealer & 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 includingx64dbg, 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]:
- Process Lineage Anomalies: Monitor Windows endpoint telemetry (such as Sysmon Event ID 1 or EDR process creation logs) for
node.exespawningdllhost.exeas a child process—especially whendllhost.exeis launched with no command-line arguments (legitimate COM surrogate instances normally include a/Processid:{GUID}argument) [1]. - 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 (
WinINetorWinHTTP) and cryptographic APIs (bcrypt.dll) [1]. - Orphaned Prebuilt Binaries: Flag npm packages that ship compiled DLLs inside a
prebuilds/directory while lacking corresponding C/C++ source files or abinding.gypbuild 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
.envfiles, 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
MicrosoftEdgeUpdateServiceand\Maiden, inspect Registry Run keys, terminate unauthorizeddllhost.exeprocesses spawned without/Processid:flags, and reimage confirmed compromised hosts [1, 3, 4].
Sources & References
- [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] 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] 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] 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/
