JScript Sequential Variable Reassignment Eval Obfuscation
What It Does
A hand-rolled JavaScript/JScript obfuscation technique that hides a real payload by splitting every character into a separate variable assignment statement. Each unique variable name is assigned a character value, then immediately overwritten multiple times in sequence. Only the last value matters at runtime. The payload string is assembled at execution time by concatenating the final values of dozens of variables inside an eval or Function call.
This defeats static string extraction because:
- No literal string in the source is longer than one character (per assignment)
- Variable names are random and reused, making simple grep for
WScriptorevalfruitless - The real payload only exists in memory after all assignments have run
Detection / Fingerprint
- Single-line JScript file with 1,000+ semicolon-delimited statements
- File size 20–50 KB (all overhead, tiny payload)
- Pattern:
varname="c";varname="h";varname="a";...where the same variable is reassigned many times - Terminal statement is a function call like
xpu()that triggers the eval - The function body contains
this[EXPR1](EXPR2)where EXPR1 resolves toeval - No
javascript-obfuscator, no dictionary lookup table, no noise padding — pure reassignment
Implementation Patterns Observed
Sequential overwrite with last-wins semantics
Observed in d3d22298 (first observed sample): ^[/intel/analyses/d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23.html]
qvpw="b";qvpw="j";qvpw="e";fijs="C";fijs="T";fijs="X";...fijs="v";
...
function xpu(){this[qvpw+fijs+baz+lvdr](fijs+baz+ghk+jrtv+...);}
xpu();
After all assignments execute, qvpw='e', fijs='v', baz='a', lvdr='l' — so this[qvpw+fijs+baz+lvdr] becomes this['eval'], and the long concatenated string is the payload passed to eval.
The payload inside the eval then uses the same technique for COM API names:
this['WScript']['CreateObject']('WScript.Shell')
where each character in 'WScript', 'CreateObject', 'WScript.Shell' is assembled from the same reassignment lookup table.
Reproduce on Your Own VMs
Create a local .js file with:
a="h";a="W";b="r";b="S";c="i";c="c";d="t";d="r";e="p";e="e";f="t";f="h";
var payload = a+b+c+d+e+f; // "Script"
WScript.Echo(payload);
Run with cscript //nologo test.js. Now scale this to 1,000+ assignments and wrap in function xpu(){this[eval](...)}.
For a full obfuscated variant:
// 1,949 sequential assignments (last-wins for 84 variables)
function xpu(){this[ev](payload);}
// ... 1,949 assignments ...
xpu();
Defensive Countermeasures
- Script content inspection: Alert on
.jsfiles with >500 semicolons and >20 KB size where the longest literal string is under 5 characters. - Process telemetry: Alert on
wscript.exeorcscript.exereadingHKCU\Control Panel\International\Locale(locale gate pattern). - Network: Block outbound HTTP to known C2 IPs from script processes.
Pages Where Observed
- unclassified-js-german-locale-dropper entity page — family overview
- unclassified-js-webdav-dropper entity page — the
fd437971andbe448b37siblings use a related "sequential variable reassignment" dialect (51–54 variables, last-wins semantics) but with WebDAV C2 instead of HTTP PHP C2 ^[/intel/analyses/fd4379716372c8a310cd664a0401a08aa5d40f94e66b381fd8aacfc29b6efb7a.html] |-d3d22298— 84 variables, 1,949 reassignments, German LCID gate, HTTP PHP C2 ^[/intel/analyses/d3d22298134d18033e55bc3581fa12d5cf30fe121d256e4044516e2bcdde4e23.html] |-771c8752— 65 variables, 192 assignments (last-wins), hybrid reassignment + noise-variable+concatenation insideFunction('return this')()[eval](...), WebDAV C294.159.113.204:8888, payload52442320412503.dll^[/intel/analyses/771c875207b6a0094f83d53e923fbc56b4577c99cc7caf92ac675455ffd15e8d.html]