JSFuck is a JavaScript obfuscation technique that rewrites any JavaScript program using only six characters: [, ], (, ), !, and +. It exploits JavaScript's implicit type coercion rules to construct strings, numbers, and function calls from these primitives. Originally created as a programming curiosity in 2010, JSFuck has become a tool for hiding malicious payloads from security scanners, with the 2025 JSFireTruck campaign infecting over 269,000 websites using this technique.
Most obfuscation techniques disguise code by renaming variables, encrypting strings, or inserting junk logic. JSFuck does something more fundamental: it rewrites the entire character set. A simple alert(1) becomes over 4,000 characters of brackets and symbols. To a human reviewer, the code is unreadable. To most security tools, it looks like noise rather than executable JavaScript. This matters because JSFuck payloads execute perfectly in any JavaScript environment. Browsers, Node.js, PDF readers with JavaScript support, even some IoT devices. The technique bypasses content filters that look for suspicious keywords like eval, document.cookie, or XMLHttpRequest. Those keywords still exist in JSFuck-encoded scripts, but they're constructed character by character from type coercion operations rather than appearing as literal strings. Understanding how JSFuck works reveals something deeper about JavaScript itself. The technique is not a bug or a hack. It is a logical consequence of design decisions made when JavaScript was created in 1995.
The Foundations: Type Coercion as a Feature
JavaScript was designed to be forgiving. When you add a number to a string, JavaScript converts the number to a string and concatenates them. When you apply a logical NOT to an array, JavaScript converts the array to a boolean first. These automatic conversions are called implicit type coercion, and they happen constantly in normal JavaScript code.
JSFuck exploits these coercions systematically. Starting from empty arrays and the operators ! and +, it builds up every character needed to write arbitrary code.
The process begins with two primitives. An empty array [] is a truthy value in JavaScript, so ![] produces false. Applying ! again gives !![], which produces true. The + operator, when applied to an array, coerces it to a number: +[] produces 0. From these starting points, every letter, digit, and symbol can be derived.
Consider how JSFuck obtains the letter "a". The value false, when coerced to a string by adding it to an empty array, becomes the string "false". Accessing index 1 of this string with bracket notation yields the character "a".
// How JSFuck derives the letter "a"
![] // false
![]+[] // "false" (coerced to string)
+!![] // 1 (true → number)
(![]+[])[+!![]] // "a" ("false"[1])This pattern repeats for every character. Each source string provides a handful of letters:
- "false" → provides f, a, l, s, e
- "true" → provides t, r, u, e
- "undefined" → provides u, n, d, e, f, i
- "NaN" → provides N (capital), a, n
- "Infinity" → provides I (capital), n, f, i, t, y
- Constructor names ("Number", "String", "Function") → remaining uppercase letters
- Number.toString(36) → remaining lowercase letters via base-36 conversion
From Forum Experiment to Weaponised Technique
JSFuck emerged from a security research competition on the sla.ckers.org forum in January 2010. Yosuke Hasegawa had recently created JJEncode, which encoded JavaScript using 18 characters. Forum members challenged each other to reduce this further. Through collaborative effort, researchers including Mario Heiderich, Gareth Heyes, and others proved that 6 characters were both necessary and sufficient.
Martin Kleppe formalised the technique in 2012, creating the jsfuck.com encoder and the GitHub repository that remains the definitive reference. For several years, JSFuck existed primarily as a curiosity, a demonstration of JavaScript's flexibility, and an occasional CTF challenge.
The technique gained security notoriety between 2015 and 2017 when researchers discovered JSFuck-encoded cross-site scripting payloads on eBay auction pages. Attackers had found that eBay's content filters blocked obvious JavaScript but allowed the seemingly random bracket sequences of JSFuck. The payloads executed when buyers viewed infected listings.
In 2025, Unit 42 researchers documented the JSFireTruck campaign, the largest known malware deployment using JSFuck obfuscation. The campaign infected 269,552 webpages in a single month, peaking at over 50,000 new infections per day. Attackers injected JSFuck-encoded redirectors into compromised WordPress sites, sending visitors to tech support scams and malware downloads. The scale demonstrated that JSFuck had evolved from academic curiosity to practical attack tool.
Most JavaScript obfuscation in active malicious campaigns uses string-array obfuscation rather than JSFuck, because the size penalty of pure JSFuck is prohibitive at commodity-malware volumes. JSFireTruck is notable precisely because it scaled JSFuck to commodity distribution despite that penalty, by encoding only a small bootstrap stub and loading the real payload externally. The two techniques attack different problems: string-array obfuscation hides the meaning of recognisable code; JSFuck hides that the file is code at all.
JSFuck is not an obscure technique. The JSFireTruck campaign infected 269,552 pages in a single month. Any JavaScript execution environment — browsers, PDF readers, server-side Node.js — will execute JSFuck payloads without modification.
How Attackers Use JSFuck in Practice
Pure JSFuck produces enormous output. The string "alert" alone requires approximately 21,000 characters. Encoding a meaningful payload would produce megabytes of brackets. Real-world attackers rarely use pure JSFuck for this reason. Instead, they combine JSFuck with other obfuscation layers:
| Campaign/Incident | Year | Scale | Technique |
|---|---|---|---|
| eBay XSS | 2015–2017 | Unknown | Pure JSFuck in auction listings |
| JSFireTruck | 2025 | 269,552 pages | JSFuck + external payload loading |
| CTF Challenges | Ongoing | N/A | Pure JSFuck or multi-layer |
| Exploit Kits | 2018–present | Varied | JSFuck decoder stubs |
Detection Challenges
JSFuck payloads share a distinctive signature: extremely high density of the six permitted characters with virtually no others. A file containing 99% brackets, parentheses, exclamation marks, and plus signs is almost certainly JSFuck. Simple statistical analysis can flag these files reliably.
The challenge comes with hybrid payloads. When JSFuck forms only 5% of a larger script, or when it is embedded in HTML attributes, or when it appears alongside legitimate minified code, the statistical signature weakens. Attackers in the JSFireTruck campaign exploited this by injecting small JSFuck stubs into large, legitimate JavaScript files where they were harder to spot.
Signature-based detection struggles because JSFuck has no fixed patterns. The same payload can be encoded millions of different ways depending on which character derivation paths the encoder chooses. Two encoders producing alert(1) will generate completely different output. Traditional antivirus signatures that match specific byte sequences are ineffective.
JSFuck's statistical signature is distinctive: 99%+ density of just six characters. Pure JSFuck is easy to flag. The real challenge is hybrid payloads where JSFuck forms only a small fraction of a larger, legitimate-looking script.
Behavioural analysis after execution catches JSFuck payloads reliably, but this approach cannot be used for static analysis of untrusted code. Sandboxing and dynamic analysis remain the gold standard, but many security workflows require static assessment before execution.
Decoding JSFuck Without Running It
JSFuck output is valid JavaScript, which means the obvious way to read it is to run it. That is also the dangerous way. Executing a JSFuck-encoded sample executes whatever malicious payload the encoder wrapped. For an analyst triaging a sample from a phishing kit or a compromised WordPress page, that is the worst possible action: the script may exfiltrate session cookies, redirect to a malware download, or pivot through whatever browser context happens to be open.
Safe decoding has to be static. The decoder must read the JSFuck source, work out what each bracket-and-symbol expression would evaluate to, and reproduce the original JavaScript without ever passing the input to a real interpreter. The technique is type coercion simulation: replicate the language's ![]-to-false, +[]-to-0, and string-indexing rules with specification-level fidelity, resolve every JSFuck character expression to its underlying letter, and reassemble the recovered string as the decoded payload. No eval(). No sandbox. No dynamic execution.
What that looks like end-to-end is straightforward: a JSFuck-encoded sample goes in as text, and the original JavaScript comes out as text. The intermediate machinery (character derivation, multi-pass reference resolution, escape-sequence handling, the edge cases that defeat most decoders) all run as static analysis, not as JavaScript.
INPUT: JSFuck-encoded payload (truncated, ~4,300 characters)
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][...]
OUTPUT: Decoded JavaScript (recovered statically, no execution)
alert('test')KlaroSkope decodes JSFuck with this static-simulation approach. Multi-layer payloads, where the JSFuck stub decodes to another obfuscation layer (most commonly Base64 or string-array obfuscation, the pattern most JSFireTruck samples use to keep file size manageable), continue through the rest of the decoder chain in the same pass. Quality scoring at every stage flags low-confidence output rather than presenting corrupted text as a successful decode.
Decode JSFuck statically. Paste a JSFuck-encoded sample at klaroskope.com/submit and KlaroSkope returns the recovered JavaScript without executing it. Hybrid samples (JSFuck stub plus Base64 or string-array inner layer, the JSFireTruck pattern) decode through the full chain in a single pass.
Why JavaScript Permits This
Other languages cannot support JSFuck-style obfuscation. Python, Ruby, and most statically typed languages would reject the type coercions that make JSFuck possible. JavaScript's permissiveness is not accidental.
Brendan Eich created JavaScript in ten days in 1995, under pressure to ship a scripting language for Netscape Navigator. The language drew inspiration from Scheme's flexibility and Self's prototype-based objects, combined with a C-like syntax to appeal to Java developers. Implicit type coercion was a deliberate choice: the language should "just work" when a web developer adds a number to a string or compares values of different types.
This design philosophy served its original purpose. JavaScript became ubiquitous precisely because it forgave sloppy code and handled type mismatches gracefully. The same flexibility that enables JSFuck also enables countless legitimate patterns that JavaScript developers use daily.
Douglas Crockford famously catalogued JavaScript's "bad parts", including many of the coercion rules that JSFuck exploits. The language community has since introduced strict mode and TypeScript to constrain these behaviours. But browsers must maintain backward compatibility with decades of existing code, so the permissive coercion rules remain available, and JSFuck continues to work.
JSFuck cannot be "fixed" without breaking the web. The type coercion rules it exploits are used by billions of lines of legitimate JavaScript. Strict mode disables some of them, but browsers must keep the permissive defaults forever. This is a permanent feature of JavaScript's attack surface.
KlaroSkope decodes JSFuck payloads automatically, handling the type coercion simulation and multi-layer unwrapping that manual analysis requires. See what your obfuscated scripts are actually doing. Try KlaroSkope Free →
Frequently Asked Questions
What does JSFuck stand for?
Is JSFuck a vulnerability in JavaScript?
Can antivirus software detect JSFuck?
How do I decode a JSFuck payload safely?
Why do attackers use JSFuck if it produces such large output?
Continue Learning
Ready to decode?
See KlaroSkope transform obfuscated scripts into actionable intelligence.
Try It Free