Safeguard
Supply Chain Security•

MALFEX on npm: Three-Year Windows Malware Campaign Moves Into a DLL

One operator has shipped Windows malware through npm since 2023. After takedowns in late September, the MALFEX loader moved into a native addon that runs on require, with no install script.

Safeguard Security Team
6 min read

On 5 October 2026, Checkmarx published its analysis of MALFEX, an npm malware campaign that CloudSEK had attributed to a single operator on 30 September. The operator has been publishing since August 2023, tied 12 packages to five accounts, and eight of them delivered Windows malware: the Overlord remote access trojan, a stealer called movinlike, or a downloader. While the takedowns were happening, a new package, imgdecode, moved the loader into a compiled Windows DLL that runs as soon as the module is loaded.

What's affected

Checkmarx lists eight malicious packages. Download figures are its counts as of 1 October, and Checkmarx notes they measure registry reach, not infections:

PackageRoleHow it runs
function-flagDownloader, 37,419 lifetime downloadspostinstall script
function-colorWrapper that installs function-flagpostinstall script
tlxbnhd, tldriver, mxdriverOverlord RAT loadersinstall hooks
cdn-img-fetch, img-to-native, native-runnerStealer chainon require, no install hook

All eight together had 40,767 downloads. On 18 October, we checked the npm registry: the three RAT loaders are unpublished, and the other five are replaced by npm's 0.0.1-security placeholder. Each has an OSV malware record, for example MAL-2026-17648 for function-flag.

Then the follow-ups. SafeDep's 13 October analysis, reported by The Hacker News, covers imgdecode, published by the account malfexteste8, and two short-lived helper packages, imgbundle and runhelper, both now gone. The four imgdecode versions were published between 30 September and 11 October. From 1.0.1 on, a native Node addon fetches an encrypted file from a GitHub repository, decrypts it with AES-256-CBC, and injects it into a hidden dllhost.exe. Version 1.0.3 drops every dependency on other MALFEX packages. The DLL exits unless it is attached to an interactive Windows console, which keeps it quiet on CI runners and in most sandboxes.

When we checked the registry and OSV on 18 October, imgdecode 1.0.0 to 1.0.3 were still published and OSV returned no record for the package. Treat it as malicious regardless.

The movinlike stealer, per Checkmarx and SafeDep, goes after Discord tokens, Chromium browser data, Telegram sessions, crypto wallets and files such as .kdbx, .pem, .ppk and .env, and offers remote shell and screenshot commands. Both firms report that the payloads fail silently on macOS and Linux.

How to check if you're exposed

Search the full dependency tree and every lock file, because Checkmarx notes malicious versions can stay pinned after a takedown:

PKGS='function-flag|function-color|tlxbnhd|tldriver|mxdriver|cdn-img-fetch|img-to-native|native-runner|imgdecode|imgbundle|runhelper'
npm ls --all 2>/dev/null | grep -E "$PKGS"
grep -rnE "\"($PKGS)\"|/($PKGS)/-/" --include=package-lock.json --include=pnpm-lock.yaml --include=yarn.lock .

Also purge them from internal registries, proxies and build caches. A registry takedown does not reach your mirror.

On Windows developer machines, check the host artifacts the researchers published. In PowerShell:

Test-Path "$env:LOCALAPPDATA\ScopeSmart Technologies Inc"
Test-Path "$env:APPDATA\Microsoft\Windows\node_runtime_helper.exe"
Test-Path "$env:APPDATA\node.exe"
Get-ScheduledTask | Where-Object TaskName -in 'Maiden','MicrosoftEdgeUpdateService'
Get-CimInstance Win32_Process -Filter "Name='dllhost.exe'" |
  ForEach-Object { $p = Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue; if ($p.Name -eq 'node') { $_ } }

A dllhost.exe whose parent is node.exe is the injection pattern SafeDep describes. Network indicators include 104.234.65.75 on ports 700 and 7007.

What to do now

  1. Block all eleven names at your registry proxy, including the wrappers. Checkmarx points out that blocking function-flag alone does not stop function-color from pulling it in.
  2. Treat any Windows machine that installed or loaded one as compromised. Isolate it and, as SafeDep advises, capture memory before rebooting, because the loader runs shellcode in memory.
  3. Rotate from a clean machine. Browser-saved passwords and sessions, Discord tokens, Telegram sessions, cloud and SSH keys, and npm and GitHub tokens. Move crypto funds to new wallets if seed phrases were on the host.
  4. Don't rely on disabling install scripts alone. It blocks the postinstall packages but not the stealer chain or imgdecode, which run on require.
  5. Don't rely on advisories alone. Checkmarx found two live packages with no advisory at all, and imgdecode had none when we looked.

Why it matters

MALFEX is not sophisticated, and the operator even signs the work: the publisher accounts, the README and an AES key all contain the name. It still lasted three years, and Orca's summary of the research says function-flag had been malicious since July 2025. Low-profile packages with plausible names get installed and rarely get read.

The move from install scripts to code that runs on import, and then into a compiled addon, is the trend to plan for. Disabling install scripts is still worth doing, but it is one layer. You also need to know when a dependency ships a native binary it didn't ship before, and to compare what you install against malware feeds continuously rather than once.

Try it yourself

List every package in one of your Node projects that ships a compiled .node file:

find node_modules -name '*.node' -type f | sed 's|/build/.*||; s|/prebuilds/.*||' | sort -u

For each, ask whether you expected native code there. Then run the project's lock file through the free scan at https://gold.safeguard.sh/scan, which checks dependencies against known malicious packages in your browser.

Click through it at your own pace, or open it full screen.

Quick check

Does npm install with ignore-scripts protect you from imgdecode?

No. imgdecode runs its native loader when the module is required, not at install time.

Why did MALFEX payloads not affect Linux CI runners?

The payloads are Windows only, and imgdecode's DLL also exits unless it is attached to an interactive Windows console.

Is blocking function-flag enough?

No. function-color depends on function-flag and pulls it in, so block every package in the campaign, including wrappers.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.