typeanalysisfamilyletsdiskusscomconfidencehighcreated2026-08-22updated2026-08-22malware-familyloaderscriptnodejsobfuscationevasionpersistence
SHA-256: 1fbaf8ab9f90dc7288b2dfa77d12c22452f162046134750036bdafc8d7500bbc

letsdiskusscom: 1fbaf8ab9f90 — Update_25.js, thirteenth sibling, thirteenth distinct msvcp140.dll morph

Executive Summary

The thirteenth confirmed sibling in the letsdiskusscom Node.js dropper cluster. A 9.0 MB JavaScript carrier (Update_25.js) masquerading as a Microsoft Edge update helper. It decodes four PE files and a BAT script from a 256-word English poem plus 164 numbered-suffix vocabulary tokens (gentle1, hush2, etc.), stages them to %ProgramData%\Microsoft Edge Updates Helper 9lra5SvMi38U, writes an HKCU Run key via the BAT, and spawns the payload. The inner EXE is a signed Revo Uninstaller Pro component; the DLL2/DLL3/BAT hashes match all twelve prior siblings, but the msvcp140.dll is a thirteenth distinct morph. No C2 from the carrier. Static-only (CAPE skipped — not a binary class). ^[triage.json] ^[file.txt]

What It Is

Field Value
SHA-256 1fbaf8ab9f90dc7288b2dfa77d12c22452f162046134750036bdafc8d7500bbc
Filename Update_25.js
File type JavaScript source, ASCII text, very long lines (63,365 chars), CRLF terminators ^[file.txt]
Size 9,015,938 bytes
Family letsdiskusscom (high confidence, 13th sibling)
Build Node.js self-contained installer, numbered-suffix poem steganography

The carrier is a 44-line Node.js script. It defines a 256-word vocabulary (wlist) with 164 numbered-suffix duplicates, encodes four PE files and a BAT as space-separated word sequences, then maps each word back to its array index to recover raw bytes. ^[strings.txt:1]

How It Works

1. Poem steganography decode

The 256-word poem vocabulary maps bytes 0x00–0xFF to words. Repeated words beyond the first cycle get numbered suffixes (gentle1 through fail164) to poison frequency analysis. The decode routine splits wlist, looks up each payload word via a.indexOf(word), and writes index & 0xFF to disk: ^[strings.txt:18]

function writePositionsToFile(listA, listB, outPath) {
  const a = listA.split(' ');
  const b = listB.split(' ');
  const positions = b.map(word => a.indexOf(word));
  const buffer = Buffer.from(positions.map(p => p & 0xFF));
  fs.writeFileSync(outPath, buffer);
}

2. Payload staging

Staged to %ProgramData%\Microsoft Edge Updates Helper 9lra5SvMi38U (suffix 9lra5SvMi38U randomized per build): ^[strings.txt:5]

File Role SHA-256 Notes
Microsoft Edge Updates Helper.exe Main payload 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f RevoSrp.exe (Registry Cleaner), signed by VS REVO GROUP OOD via DigiCert Trusted G4 Code Signing CA
msvcp140.dll VC++ runtime ce73fdede936589ca432eefcafedd24eb8035e09247973287b217c3482153c09 Thirteenth distinct morph (1,089,536 B), MSVC 14.27.29016.0, timestamp 1592277074 (2020-06-16)
vcruntime140.dll VC++ runtime ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 Same as all prior siblings
vcruntime140_1.dll VC++ runtime 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 Same as all prior siblings
9lra5SvMi38U.bat Persistence launcher dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 Same as all prior siblings

3. Persistence and execution

The BAT adds an HKCU Run registry entry for the EXE path, then exits. The carrier spawns the BAT with the EXE path as an argument, then spawns the EXE directly: ^[strings.txt:40]

launchExecutable(`"${autorunPath}"`, [`"${exePath}"`]);
launchExecutable(`"${exePath}"`);

Both spawns use child_process.spawn(..., { shell: true, stdio: 'inherit' }). ^[strings.txt:30]

4. Inner EXE analysis

The decoded EXE (8b94af60...) is a 51,424-byte x64 PE with a valid Authenticode signature by VS REVO GROUP OOD (DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1). PDB path: D:\Work_REVO\VSRevo\Windows\Projects\Registry Cleaner\revo-registry-cleaner\Revo Registry Cleaner\x64\Release\RevoSrp.pdb. Imports are benign: KERNEL32 (LoadLibraryW, GetProcAddress), ADVAPI32 (registry APIs), OLE32, MSVCP140, VCRUNTIME140. No network imports. No C2 surface. This is a legitimate signed tool repurposed as a masquerade payload.

Decompiled Behavior

Not applicable — the threat is the carrier script, not the inner EXE. The carrier's control flow is linear: decode → mkdir → writeFileSync × 5 → spawn × 2. No branching, no anti-analysis, no sandbox gates. The obfuscation is entirely lexical (poem steganography).

C2 Infrastructure

None from the carrier. The inner EXE has no observable C2 surface. The threat model is social-engineering delivery + silent local installation + persistence, not remote command-and-control.

Interesting Tidbits

  • Filename counter: Update_25.js continues the internal build counter; prior siblings include Update_3.js, Update_13.js, Update_22.js, and now Update_25.js — confirming active campaign iteration. ^[triage.json]
  • Thirteenth distinct msvcp140.dll: The builder rotates this DLL per campaign while keeping the EXE, two vcruntime DLLs, and BAT identical. This is supply-chain artifact collection or deliberate hash-diversity against IOC-based blocking. The DLL is a legitimate Microsoft VC++ runtime (MSVC 14.27, 2020-06-16), not attacker-compiled.
  • dll4Path typo: The script declares const dll4Path = path.join(folder, "9lra5SvMi38U.bat") — a copy-paste error (reuses the BAT filename as dll4Path), but dll4Path is never referenced in the execution block. Dead code from a builder template. ^[strings.txt:17]
  • No base64, no hex: The steganography is purely word-index mapping. Static tools that hunt for base64 blobs or hex strings will miss this entirely. ^[strings.txt:1]
  • Same spawn mode as c075aeba: Uses { shell: true, stdio: 'inherit' } rather than { detached: true }. The cluster oscillates between spawn modes; not a reliable family discriminator.

How To Mess With It (Homelab Replication)

  1. Poem encoder (Python):
    with open('words.txt') as f: words = f.read().split()
    assert len(words) == 256
    with open('payload.exe', 'rb') as f: data = f.read()
    encoded = ' '.join(words[b] for b in data)
    # Add numbered suffixes for the variant
    
  2. Carrier template (Node.js): Wrap the word list and encoded strings in the writePositionsToFile + spawn pattern shown above.
  3. Verification: Decode back to the original file byte-for-byte. The MZ header (4d5a) should appear as gentle unwearied (indices 0, 78) in the encoded stream.

Deployable Signatures

YARA rule

rule letsdiskusscom_js_dropper {
    meta:
        description = "Node.js poem-steganography dropper (letsdiskusscom cluster)"
        author = "PacketPursuit"
        date = "2026-08-22"
        hash = "1fbaf8ab9f90dc7288b2dfa77d12c22452f162046134750036bdafc8d7500bbc"
    strings:
        $s1 = "const fs = require('fs')" ascii
        $s2 = "const { spawn } = require('child_process')" ascii
        $s3 = "Microsoft Edge Updates Helper" ascii
        $s4 = "writePositionsToFile" ascii
        $s5 = "Buffer.from(positions.map(p => p & 0xFF))" ascii
        $poem1 = "gentle hush that wraps the midnight air" ascii
        $poem2 = "moonlight stitches silver on sea" ascii
        $poem3 = "let mortal sorrow find a softer chair" ascii
    condition:
        filesize > 1MB and
        3 of ($s*) and
        2 of ($poem*)
}

Sigma rule

title: Node.js Poem Steganography Dropper Execution
status: experimental
description: Detects Node.js process writing PE files to ProgramData and spawning child processes
logsource:
    product: windows
    category: process_creation
detection:
    selection:
        CommandLine|contains: 'node'
        ParentImage|endswith:
            - '\node.exe'
            - '\nodejs.exe'
    child_spawn:
        Image|endswith:
            - '\Microsoft Edge Updates Helper.exe'
            - '.bat'
    condition: selection and child_spawn
falsepositives:
    - Legitimate Node.js applications
level: high

IOC list

Type Value Context
SHA-256 (carrier) 1fbaf8ab9f90dc7288b2dfa77d12c22452f162046134750036bdafc8d7500bbc Update_25.js
SHA-256 (EXE) 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f RevoSrp.exe
SHA-256 (DLL1) ce73fdede936589ca432eefcafedd24eb8035e09247973287b217c3482153c09 msvcp140.dll (13th morph)
SHA-256 (DLL2) ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 vcruntime140.dll
SHA-256 (DLL3) 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 vcruntime140_1.dll
SHA-256 (BAT) dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 autorun.bat
File path %ProgramData%\Microsoft Edge Updates Helper 9lra5SvMi38U\* Staging directory
Registry HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Microsoft Edge Updates Helper Persistence

Behavioral fingerprint statement

This JavaScript carrier (Node.js) defines a 256-word English poem vocabulary and encodes PE payloads as space-separated word sequences. At runtime it decodes words to array indices, writes the indices as bytes to %ProgramData%\Microsoft Edge Updates Helper <random_suffix>, drops a BAT file that writes an HKCU Run registry key, and spawns both the BAT and the EXE via child_process.spawn with shell: true. No network C2. The inner EXE is a signed legitimate tool (Revo Uninstaller Pro component) with no malicious imports.

Detection Signatures

ATT&CK ID Technique Evidence
T1059.007 JavaScript execution Node.js require('fs') + require('child_process') ^[strings.txt:1]
T1027.002 Obfuscated Files or Info poem-word-list-steganography — 256-word English poem with numbered suffixes ^[strings.txt:1]
T1036.005 Masquerading Microsoft Edge Updates Helper directory and filename ^[strings.txt:4]
T1547.001 Registry Run Keys BAT calls reg add on HKCU\Software\Microsoft\Windows\CurrentVersion\Run ^[strings.txt:1]
T1543.003 Create/modify system process child_process.spawn(..., {shell: true, stdio: 'inherit'}) ^[strings.txt:30]

References

Provenance

Analysis derived from file.txt (file type), triage.json (metadata), strings.txt (JavaScript source extraction), and manual payload decoding via Python/Node.js reimplementation of the writePositionsToFile routine. Payload hashes verified against VirusTotal. No dynamic execution performed (CAPE skipped — JS source not a supported binary class). Sample binary at <sample 1fbaf8ab9f90.bin>.