javascript-obfuscator
Commercial-grade JavaScript obfuscation produced by the javascript-obfuscator npm package (open-source, widely abused by malware authors). Recognisable by three structural features that survive minification:
- String-array lookup table — all literal strings are hoisted into a single large array (
var _0x... = [...]) and referenced by numeric index. Array length commonly 500–3,000+ entries. - Encoding + encryption — array entries are base64-encoded with a swapped-case custom alphabet (
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=) and then RC4-encrypted with a per-call key (the second argument of the decoder function). - Control-flow flattening (CFF) — the original sequential logic is replaced by a
while(!![]){ try{...} catch{...} }dispatch loop where each iteration evaluates dead arithmetic to decide which "branch" (acase-like block) executes next.
Anti-debug add-on. When the --self-defending flag is enabled, the obfuscator injects a constructor that checks Function.prototype.toString() against a regex. If a debugger modifies the function body, the regex match fails, a counter decrements, and the script enters a mutation loop that corrupts internal state.
Recovery. Decoding requires re-implementing the exact decoder function (including the custom base64 alphabet and RC4 key schedule) or running the script in a sandboxed WScript/Node environment. Tools like floss and capa do not handle this variant because it is not a PE binary.
Observed in
unclassified-js-pptx-dropper(9a69ad1b) — RC4 string-array + plain-HTTP IP C2.unclassified-js-rentry-telegram-dropper(b0c43e946344) — 190-entry array + Telegram Bot API exfil.- Sample
6f4de3f9— 2,844-entry array,disable-and-install.jspayload, SmartScreen disable + schtasks persistence. ^[/intel/analyses/6f4de3f972e1acf8ca603ff87b65ab4b92abf303f98b87cc7e822bfd5828d132.html]