typetechniqueconfidencehighcreated2026-07-19updated2026-07-22obfuscationscriptdefense-evasionevasionjscriptjavascript

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:

  1. No literal string in the source is longer than one character (per assignment)
  2. Variable names are random and reused, making simple grep for WScript or eval fruitless
  3. 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 to eval
  • 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 .js files with >500 semicolons and >20 KB size where the longest literal string is under 5 characters.
  • Process telemetry: Alert on wscript.exe or cscript.exe reading HKCU\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 fd437971 and be448b37 siblings 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 inside Function('return this')()[eval](...), WebDAV C2 94.159.113.204:8888, payload 52442320412503.dll ^[/intel/analyses/771c875207b6a0094f83d53e923fbc56b4577c99cc7caf92ac675455ffd15e8d.html]