typeanalysisfamilyletsdiskusscomconfidencehighcreated2026-08-22updated2026-08-22malware-familyloaderscriptnodejsobfuscationevasionpersistence
SHA-256: ff3ae2e72f50037153c943205f4d6fa90a756763e15b2cb8a5d09f8afeb06253

letsdiskusscom: ff3ae2e72f50 — Update_22.js, twelfth sibling, twelfth distinct msvcp140.dll morph

Executive Summary

The twelfth confirmed sibling in the letsdiskusscom Node.js dropper cluster. A 9.0 MB JavaScript carrier (Update_22.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 cs3GSg6r7qBT, 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 prior siblings, but the msvcp140.dll is a twelfth 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 ff3ae2e72f50037153c943205f4d6fa90a756763e15b2cb8a5d09f8afeb06253
Filename Update_22.js
File type JavaScript source, ASCII text, very long lines (63,365 chars), CRLF terminators ^[file.txt]
Size 9,075,792 bytes
Family letsdiskusscom (high confidence, 12th sibling)
Build Node.js self-contained installer, numbered-suffix poem steganography

The carrier is a 60-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:1]

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 cs3GSg6r7qBT (suffix cs3GSg6r7qBT randomized per build):

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 24abac6fbf8b880eff96fb250a205ccf78ba91d707b83a1b1810458f55179f49 Twelfth distinct morph (1,097,728 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
cs3GSg6r7qBT.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:

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

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

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_22.js implies an internal build counter; prior siblings include Update_3.js, Update_13.js, and Update_22.js — suggesting active campaign iteration. ^[triage.json]
  • Twelfth distinct msvcp140.dll: The builder rotates this DLL per campaign while keeping the EXE, two vcruntime DLLs, and BAT identical. This is either supply-chain artifact collection or deliberate hash-diversity against IOC-based blocking.
  • dll4Path typo: The script declares const dll4Path = path.join(folder, "cs3GSg6r7qBT.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:1]
  • 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.

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_nodejs_poem_dropper {
    meta:
        description = "Node.js dropper with poem-word-list steganography (letsdiskusscom cluster)"
        author = "PacketPursuit"
        reference = "raw/analyses/ff3ae2e72f50037153c943205f4d6fa90a756763e15b2cb8a5d09f8afeb06253"
    strings:
        $a = "Microsoft Edge Updates Helper" ascii wide
        $b = "writePositionsToFile" ascii wide
        $c = "Buffer.from(positions.map" ascii wide
        $d = /const wlist\s*=\s*"[a-z0-9 -]+"/ ascii
        $e = "fs.writeFileSync(outPath, buffer)" ascii wide
        $f = "child_process" ascii wide
    condition:
        filesize > 1MB and
        uint32be(0) != 0x4d5a5a4d and  // not a PE
        4 of ($a, $b, $c, $d, $e, $f)
}

Sigma rule

title: letsdiskusscom Node.js Poem Dropper Execution
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    CommandLine|contains:
      - 'Microsoft Edge Updates Helper'
      - 'cs3GSg6r7qBT.bat'
    ParentImage|endswith: '\node.exe'
  condition: selection
falsepositives:
  - None expected
level: high

IOC list

Type Value Context
Filename Update_22.js Carrier script
Directory %ProgramData%\Microsoft Edge Updates Helper cs3GSg6r7qBT Staging
Registry HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Microsoft Edge Updates Helper Persistence
SHA-256 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f Inner EXE (RevoSrp)
SHA-256 24abac6fbf8b880eff96fb250a205ccf78ba91d707b83a1b1810458f55179f49 msvcp140.dll (12th morph)
SHA-256 ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 vcruntime140.dll
SHA-256 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 vcruntime140_1.dll
SHA-256 dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 BAT persistence script

Behavioral fingerprint

A Node.js process writes five files to a %ProgramData% subdirectory named Microsoft Edge Updates Helper <random_suffix>: a 52 KB x64 EXE with DigiCert signature, three VC++ runtime DLLs (one varying per campaign), and a 440-byte BAT that adds an HKCU Run key. The Node.js process then spawns the BAT and the EXE via child_process.spawn with shell: true. No network traffic from the carrier.

Detection Signatures

  • ATT&CK T1059.007 — JavaScript execution via Node.js require('child_process') ^[triage.json]
  • ATT&CK T1027.002 — Obfuscated Files or Info: numbered-suffix poem-word-list steganography ^[strings.txt:1]
  • ATT&CK T1036.005 — Masquerading: Microsoft Edge Updates Helper directory name ^[strings.txt:1]
  • ATT&CK T1547.001 — Registry Run Keys via BAT reg add ^[strings.txt:1]
  • ATT&CK T1543.003 — Create/modify system process via child_process.spawn ^[strings.txt:1]

References

Provenance

  • File type: file utility on ff3ae2e72f50.bin → JavaScript source, ASCII text, with very long lines (63365), with CRLF line terminators ^[file.txt]
  • Triage: custom triage pipeline (triage.json) ^[triage.json]
  • Strings: strings -n 8 on the raw JS produced 9,075,697 bytes of output, confirming the poem-vocabulary repetition ^[strings.txt]
  • capa: skipped — not a supported binary class ^[capa.txt]
  • CAPE: skipped — file type not a supported binary class for detonation ^[dynamic-analysis.md]
  • Manual decode: Python script recovered five embedded files by word-index mapping; pefile and strings analysis performed on decoded EXE/DLLs. Inner EXE signature verified via embedded Authenticode PKCS#7 structures (DigiCert).