Fundamentals27-Jan-26|10 min read

JSFuck Obfuscation: How It Works and How to Decode It

How six characters exploit JavaScript's type system to hide malicious code, and how to decode it safely

Definition:

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".

javascript
// 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:

JSFuck decoder stub + Base64 payloadThe outer JSFuck layer constructs just enough code to call atob() and eval() on a Base64-encoded inner payload. This keeps file sizes manageable while still evading keyword-based detection. The security scanner sees no recognisable function names.
Bootstrap loader injectionThe JSFireTruck campaign encoded only the critical bootstrap code in JSFuck — a small stub that loads the real payload from an external server. Small enough to inject into HTML comments or script tags without dramatically increasing page size.
Selective function name encodingRather than encoding entire payloads, attackers encode only the function names that trigger security filters: eval, Function, document.write. The rest of the script remains readable, but the critical execution mechanism is hidden.
Real-World JSFuck Campaigns
eBay XSS
Year2015–2017
ScaleUnknown
TechniquePure JSFuck in auction listings
JSFireTruck
Year2025
Scale269,552 pages
TechniqueJSFuck + external payload loading
CTF Challenges
YearOngoing
ScaleN/A
TechniquePure JSFuck or multi-layer
Exploit Kits
Year2018–present
ScaleVaried
TechniqueJSFuck 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.

text
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

Q

What does JSFuck stand for?

JSFuck is a portmanteau of "JavaScript" and an expletive, reflecting the incredulous reaction most developers have when they first encounter it. The name was coined by the security research community on the sla.ckers.org forum around 2010. Some security vendors use the sanitised name "JSF*ck" in published research.
Q

Is JSFuck a vulnerability in JavaScript?

No. JSFuck exploits documented, intentional features of JavaScript's type coercion system. It is not a bug, exploit, or vulnerability in the language. JavaScript behaves exactly as specified when executing JSFuck code. The technique is better understood as a creative (mis)use of language features rather than a security flaw.
Q

Can antivirus software detect JSFuck?

Traditional signature-based antivirus struggles with JSFuck because the same payload can be encoded in countless different ways. Modern security tools use a combination of statistical analysis (detecting the distinctive character distribution), behavioural sandboxing (executing code and observing its actions), and heuristic rules. Pure JSFuck is relatively easy to flag; hybrid payloads that combine JSFuck with other techniques are harder to detect statically.
Q

How do I decode a JSFuck payload safely?

Never execute untrusted JSFuck directly, as this runs the hidden payload. Static deobfuscation tools attempt to decode JSFuck without execution by simulating JavaScript's type coercion rules. However, these tools have limitations with large files, variant encodings, and multi-layer obfuscation. KlaroSkope provides automated JSFuck decoding with quality scoring to assess output validity.
Q

Why do attackers use JSFuck if it produces such large output?

Pure JSFuck produces enormous files, but attackers typically use it selectively. Common patterns include encoding only a small loader stub in JSFuck while keeping the main payload in faster-to-decode formats like Base64, or using JSFuck to construct just the critical function names (eval, Function) that would otherwise trigger security filters.

Found this useful? Sharing is caring!

Ready to decode?

See KlaroSkope transform obfuscated scripts into actionable intelligence.

Try It Free