3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357letsdiskusscom: 3465e6ee — Numbered-suffix poem steganography with new msvcp140.dll morph
Executive Summary
Sixth confirmed sibling of the letsdiskusscom Node.js dropper cluster. Uses the same 256-word English poem lookup-table steganography as siblings d0ca14b3/247b54b5/af4313e4/c075aeba, but introduces numbered suffixes on repeated vocabulary (e.g. gentle1, hush2) to defeat naive word-list deduplication. Drops the same signed RevoSrp.exe and two vcruntime DLLs as all prior siblings, but carries a sixth distinct msvcp140.dll morph (5975596f...). Self-contained local installer with BAT-based HKCU Run persistence.
What It Is
| Field | Value |
|---|---|
| SHA-256 | 3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357 |
| Filename | Update_20.js |
| Size | 7.9 MB (7,883,546 bytes) |
| Type | JavaScript source, ASCII text, CRLF line terminators ^[file.txt] |
| Language | Node.js (requires fs, path, child_process) |
No PE header. Not a supported binary class for CAPE; dynamic analysis skipped. ^[dynamic-analysis.md]
How It Works
- Obfuscation: A 256-word poem serves as a byte lookup table (
wlist). Four PE files plus a BAT script are encoded as sequences of poem-word indices. ^[strings.txt:6] - Decoding:
writePositionsToFilesplits the word list, looks up each payload word's index, and writesindex & 0xFFto disk viaBuffer.from. ^[strings.txt:18-25] - Staging: Creates
%ProgramData%\Microsoft Edge Updates Helper w3lo1kUOohHh\and drops:Microsoft Edge Updates Helper.exe— RevoSrp.exe (52,400 bytes, signed) ^[strings.txt:12]msvcp140.dll— VC++ runtime (932,864 bytes, new morph) ^[strings.txt:14]vcruntime140.dll— same as prior siblings (101,672 bytes) ^[strings.txt:15]vcruntime140_1.dll— same as prior siblings (44,328 bytes) ^[strings.txt:16]w3lo1kUOohHh.bat— persistence + launcher (440 bytes) ^[strings.txt:13]
- Execution: Calls
launchExecutabletwice — first the BAT (which writes HKCU Run and launches the EXE), then the EXE directly. ^[strings.txt:40-41]
Decompiled Behavior
No native code to decompile. The JavaScript is delivered in near-plaintext; obfuscation is lexical rather than algorithmic. The payload reconstruction logic (decoded from strings.txt lines 18-25) is:
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);
}
This is the same decoder pattern observed in all poem-lookup siblings.
C2 Infrastructure
None in the carrier. The Node.js script is entirely self-contained; no network APIs are imported. The signed RevoSrp.exe payload may contain its own update-check logic, but no C2 was recovered from the carrier or the decoded PE statically.
Interesting Tidbits
- Numbered suffix builder variant: The poem repeats after ~92 words; from index 92 onward each word gains a numeric suffix (
gentle1,hush2,that3...fail164). This is a builder-level change intended to poison word-frequency analysis and prevent analysts from quickly deduplicating the vocabulary. First observed in this sibling. ^[strings.txt:6] - Sixth msvcp140.dll morph: The
msvcp140.dllSHA-256 (5975596f...) does not match any of the five prior siblings. The EXE and both vcruntime DLLs remain identical across all six siblings, suggesting the builder randomizes or cycles the VC++ runtime redistributable. ^[strings.txt:14] - dll4Path bug: Line 17 assigns
dll4Pathto the BAT filename (w3lo1kUOohHh.bat), but the variable is never consumed. The BAT is written usingautorunPath. Dead code from builder template drift. ^[strings.txt:17] - RevoSrp.exe: Decoded EXE is
Revo Registry Cleaner\x64\Release\RevoSrp.pdb, signed by VS REVO GROUP OOD via DigiCert. Legitimate software abused as payload carrier.
How To Mess With It (Homelab Replication)
- Poem table: Write a 256-word prose fragment. Optionally append numeric suffixes to repeated words.
- Encode: For each byte of payload, emit the corresponding word.
- Carrier: Wrap in Node.js with the
writePositionsToFilepattern. - Verify: Decode back to original bytes; compare SHA-256.
Learn: how lexical steganography evades string-based detection and why word-list deduplication must strip suffixes first.
Deployable Signatures
YARA
rule letsdiskusscom_poem_dropper {
meta:
description = "Node.js dropper using 256-word poem lookup-table steganography"
author = "PacketPursuit"
date = "2026-08-21"
sha256 = "3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357"
strings:
$node_fs = "const fs = require('fs');"
$node_path = "const path = require('path');"
$node_spawn = "const { spawn } = require('child_process');"
$app_name = "Microsoft Edge Updates Helper"
$wlist = "const wlist = \""
$func = "function writePositionsToFile(listA, listB, outPath)"
$safe = "function safeMakeDir(dir)"
$launch = "function launchExecutable(execPath, args = [])"
condition:
filesize > 1MB and
all of ($node_*) and
$app_name and
$wlist and
$func and
$safe and
$launch
}
Sigma
title: Letsdiskusscom Node.js Poem Dropper Execution
logsource:
category: process_creation
product: windows
detection:
selection_js:
CommandLine|contains:
- 'node.exe'
- 'Update_20.js'
selection_child:
ParentImage|endswith: '\\node.exe'
Image|endswith:
- '\\Microsoft Edge Updates Helper.exe'
- '\\w3lo1kUOohHh.bat'
selection_dir:
CommandLine|contains: 'Microsoft Edge Updates Helper w3lo1kUOohHh'
condition: selection_js or selection_child or selection_dir
falsepositives:
- Unknown
level: high
IOCs
| Indicator | Value | Type |
|---|---|---|
| Carrier SHA-256 | 3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357 |
hash |
| Decoded EXE SHA-256 | 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f |
hash |
| Decoded DLL1 (msvcp140) SHA-256 | 5975596f81f4600baeb70a6eeb308d5465e83d42e9e96df7e45b411fc0d2a61d |
hash |
| Decoded DLL2 (vcruntime140) SHA-256 | ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 |
hash |
| Decoded DLL3 (vcruntime140_1) SHA-256 | 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 |
hash |
| Decoded BAT SHA-256 | dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 |
hash |
| Staging directory | %ProgramData%\Microsoft Edge Updates Helper w3lo1kUOohHh\ |
path |
| Registry Run value | Microsoft Edge Updates Helper |
regkey |
| Revo EXE PDB path | D:\Work_REVO\VSRevo\Windows\Projects\Registry Cleaner\revo-registry-cleaner\Revo Registry Cleaner\x64\Release\RevoSrp.pdb |
string |
| Signed by | VS REVO GROUP OOD (DigiCert) |
cert |
Behavioral Fingerprint
A Node.js process reads a multi-megabyte JavaScript file containing two very long space-separated word strings. It creates a Microsoft Edge Updates Helper-named directory under %ProgramData%, writes five files (EXE + three DLLs + BAT), spawns a batch script that writes HKCU\Software\Microsoft\Windows\CurrentVersion\Run, then spawns the EXE. No network connections from the carrier script.
Detection Signatures
- capa: Not applicable — input is JavaScript source, not a supported executable format. ^[capa.txt]
- floss: Not applicable. ^[floss.txt]
References
- letsdiskusscom — entity page for the cluster
- poem-word-list-steganography — technique page for the 256-word poem encoding
- registry-run-persistence — procedure page for BAT-based HKCU Run persistence
- natural-language-payload-encoding — broader concept
- Sibling analysis:
/intel/analyses/d0ca14b3ad12100898d69afacfecfbdb186fe1bd801f69aecf355413bf6e502b.html - Sibling analysis:
/intel/analyses/247b54b524dcdd1a4dbe76ac11473ba26ea003193ad86216fe411f9b80e8c7fb.html
Provenance
file.txt,strings.txt,capa.txt,floss.txt,dynamic-analysis.md— generated by triage pipeline (file, strings, capa, floss)- Decoded PE payloads verified with
fileandpefile.pyon host - SHA-256 computed with
sha256sum - Poem and payload extraction performed via Python regex on 2026-08-21