ddcb25ee4715a186558c8bf9540929049d9d69e0fe23d15ef885e71327d217a1letsdiskusscom: ddcb25ee — Eighth confirmed sibling, eighth distinct msvcp140.dll morph
Eighth confirmed sibling of the letsdiskusscom Node.js dropper cluster. Identical 256-word English poem lookup-table steganography, identical signed RevoSrp.exe payload (SHA-256 8b94af60...), identical BAT-based HKCU\Run persistence script, and identical vcruntime DLLs — but an eighth distinct msvcp140.dll morph (SHA-256 696b6350..., 889,344 bytes, MSVC 14.40.33807.0). Builds on the existing seven-sibling cluster described in letsdiskusscom. ^[strings.txt:1]
What It Is
- File:
Update_7.js, 7,563,833 bytes, JavaScript source, ASCII, CRLF line terminators. ^[file.txt] - SHA-256:
ddcb25ee4715a186558c8bf9540929049d9d69e0fe23d15ef885e71327d217a1 - Family:
letsdiskusscom(eighth confirmed sibling; high confidence) - Build stack: Plain Node.js (no
javascript-obfuscator);require('fs'),require('path'),require('child_process'). - Payload encoding: 256-word English poem lookup-table steganography (the same poem used across all poem-stego siblings, with numbered-suffix variant in
3465e6ee). Each payload byte is encoded as the index of a poem word; decoded by mapping word back to index and masking to& 0xFF. ^[strings.txt:3]
How It Works
Poem-Lookup Steganography
The script declares wlist — a 256-word English poem in sequence. Five payload strings (exe, bat, dll1, dll2, dll3) are expressed as space-delimited word sequences drawn from this list. At runtime, writePositionsToFile() maps each payload word back to its index in wlist, masks to 0xFF, writes to a Buffer, and persists to disk. ^[strings.txt:3]
This is the same technique documented in poem-word-list-steganography and observed across siblings d0ca14b3, 247b54b5, af4313e4, c075aeba, 3465e6ee, and 5126076d. This sample reverts to the plain 256-word list (no numbered suffixes), matching siblings d0ca14b3 through c075aeba and 5126076d, and diverging from 3465e6ee which introduced gentle1, hush2, etc.
Staging Directory
%ProgramData%\Microsoft Edge Updates Helper XQ49JB98nU3B\
Same directory name suffix (XQ49JB98nU3B) across all eight siblings. ^[strings.txt:4]
Dropped Files (decoded)
| File | Size | SHA-256 | Notes |
|---|---|---|---|
Microsoft Edge Updates Helper.exe |
52,400 | 8b94af60... |
RevoSrp.exe (VS Revo Group, signed, MSVC 14.44, PE32+ x64). Same hash across all eight siblings. ^[strings.txt:5] |
msvcp140.dll |
889,344 | 696b6350... |
Eighth distinct morph. MSVC 14.40.33807.0. Timestamp 0x683d5af8 (2025-06-02). ^[strings.txt:6] |
vcruntime140.dll |
101,672 | ff43e813... |
Same hash across all eight siblings. ^[strings.txt:7] |
vcruntime140_1.dll |
44,328 | 7b8f70dd... |
Same hash across all eight siblings. ^[strings.txt:8] |
XQ49JB98nU3B.bat |
440 | dff20059... |
Same BAT hash across all eight siblings. Adds HKCU\Software\Microsoft\Windows\CurrentVersion\Run persistence. ^[strings.txt:9] |
Execution Flow
- Create
%ProgramData%\Microsoft Edge Updates Helper XQ49JB98nU3B. - Decode and write five files via
writePositionsToFile(). - Launch the BAT with the EXE path as argument (
launchExecutable(autorunPath, [exePath])). - The BAT adds the EXE to
HKCU\...\Runviareg add, then exits. - The JS then launches the EXE directly (
launchExecutable(exePath)).
Steps 3 and 4 both call spawn(..., { shell: true }). ^[strings.txt:10]
Decompiled Behavior
Not applicable — this is a plaintext JavaScript source file, not a compiled PE. The entire logic is visible in strings.txt. No Ghidra or radare2 analysis required.
C2 Infrastructure
None observed. This is a self-contained local installer — no network calls, no download URLs, no C2 beaconing in the carrier script. The RevoSrp.exe inner payload may contain its own C2 surface (not analyzed statically in this session). ^[strings.txt:11]
Interesting Tidbits
- Eight msvcp140.dll morphs, one cluster: The builder appears to randomize or select from a pool of VC++ runtime redistributables while keeping the core payload (RevoSrp.exe) identical. This suggests an automated build pipeline that swaps dependency DLLs but pins the malicious EXE. ^[entities/letsdiskusscom.md]
- No obfuscation on the JS: Unlike sibling
9dc2cdedwhich usedjavascript-obfuscator, this sample is plain Node.js. The only anti-static measure is the sheer size (7.5 MB) and the poem encoding. - Version-info masquerade: The inner RevoSrp.exe carries
CompanyName: VS Revo GroupandProductName: RevoSrp, masquerading as a legitimate uninstaller component. ^[exiftool.json] - dll4 = dll1? The script declares
const dll4Path = path.join(folder, "XQ49JB98nU3B.bat")butdll4is never defined in the payloads — likely a copy-paste artefact from the builder template. ^[strings.txt:12] - CAPE skipped: File type is JavaScript source, not a supported binary class for detonation. ^[dynamic-analysis.md]
How To Mess With It (Homelab Replication)
Build a proof-of-concept poem encoder in Python:
import struct
wlist = "gentle hush that wraps ...".split() # 256 words
assert len(wlist) == 256
def encode_file(path):
with open(path, 'rb') as f:
data = f.read()
words = [wlist[b] for b in data]
return ' '.join(words)
exe_words = encode_file('/path/to/payload.exe')
# Write exe_words into a JS template matching the original
Verification: The decoded EXE should be a 52 KB PE32+ x64 with VS Revo Group version info and a valid Authenticode signature.
Deployable Signatures
YARA rule
rule letsdiskusscom_poem_stego_js
{
meta:
description = "Letsdiskusscom Node.js dropper with 256-word poem lookup-table steganography"
author = "PacketPursuit"
date = "2026-08-22"
hash = "ddcb25ee4715a186558c8bf9540929049d9d69e0fe23d15ef885e71327d217a1"
family = "letsdiskusscom"
strings:
$a = "Microsoft Edge Updates Helper XQ49JB98nU3B"
$b = "const fs = require('fs');"
$c = "const { spawn } = require('child_process');"
$d = "writePositionsToFile"
$e = "wlist = \"gentle hush that wraps the midnight air"
$f = "XQ49JB98nU3B.bat"
$g = "Microsoft Edge Updates Helper.exe"
condition:
filesize > 7MB and
4 of ($a,$b,$c,$d,$e,$f,$g)
}
Behavioral hunt query (Sigma)
title: Letsdiskusscom Node.js Dropper Execution
logsource:
product: windows
category: process_creation
detection:
selection:
CommandLine|contains:
- 'Microsoft Edge Updates Helper XQ49JB98nU3B'
- 'XQ49JB98nU3B.bat'
condition: selection
falsepositives:
- None expected — the directory name and batch filename are unique to this cluster.
level: critical
IOC list
| Indicator | Value | Type |
|---|---|---|
| Carrier SHA-256 | ddcb25ee4715a186558c8bf9540929049d9d69e0fe23d15ef885e71327d217a1 |
Hash |
| Inner EXE SHA-256 | 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f |
Hash |
| msvcp140.dll SHA-256 | 696b635099d5f7e60d432475674cb7fc0d7286ea5bd5472ade1bacfc07e6f355 |
Hash |
| BAT SHA-256 | dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 |
Hash |
| Staging dir | %ProgramData%\Microsoft Edge Updates Helper XQ49JB98nU3B\ |
Path |
| Registry key | HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Microsoft Edge Updates Helper |
Registry |
| App name | Microsoft Edge Updates Helper |
String |
Behavioral fingerprint
This JavaScript carrier is a self-contained Node.js installer that decodes five embedded payloads from a 256-word English poem lookup table, stages them to a fake Microsoft Edge Updates Helper directory under %ProgramData%, adds the dropped EXE to the current-user Run key via a BAT script, and launches it with { shell: true }. No network calls in the carrier. The inner EXE is always a signed 52 KB RevoSrp.exe (VS Revo Group, MSVC 14.44) with varying msvcp140.dll redistributable morphs.
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 | 256-word poem lookup-table steganography ^[strings.txt:3] |
| T1036.005 | Masquerading | Microsoft Edge Updates Helper directory and registry value ^[strings.txt:4] |
| T1547.001 | Registry Run Keys | BAT writes HKCU\...\Run for persistence ^[strings.txt:9] |
| T1543.003 | Create/modify system process | child_process.spawn(..., { shell: true }) ^[strings.txt:10] |
References
- Sibling analysis: /intel/analyses/d0ca14b3ad12100898d69afacfecfbdb186fe1bd801f69aecf355413bf6e502b.html
- Sibling analysis: /intel/analyses/5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e.html
- Entity page: letsdiskusscom
- Technique page: poem-word-list-steganography
- Concept page: natural-language-payload-encoding
- Procedure page: registry-run-persistence
Provenance
file.txt— file(1) output (file v5.44)strings.txt— strings -n 4 output (GNU binutils strings 2.42)exiftool.json— ExifTool 12.76 JSON exportdynamic-analysis.md— CAPE skipped (file type not supported)- Decoded payloads verified via custom Python decoder against the 256-word
wlistand compared to known sibling hashes.