ff3ae2e72f50037153c943205f4d6fa90a756763e15b2cb8a5d09f8afeb06253letsdiskusscom: 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.jsimplies an internal build counter; prior siblings includeUpdate_3.js,Update_13.js, andUpdate_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 asdll4Path), butdll4Pathis 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)
- 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 - Carrier template (Node.js): Wrap the word list and encoded strings in the
writePositionsToFile+spawnpattern shown above. - Verification: Decode back to the original file byte-for-byte. The MZ header (
4d5a) should appear asgentle 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 Helperdirectory 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
- letsdiskusscom — Entity page for the family
- poem-word-list-steganography — Technique page for the encoding
- natural-language-payload-encoding — Concept page
- registry-run-persistence — Procedure page for BAT-based HKCU Run
- Artifact ID:
d74e7d91-afa5-4838-9570-8232b6cf74c7(MalwareBazaar via OpenCTI)
Provenance
- File type:
fileutility onff3ae2e72f50.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 8on 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).