This subset of JavaScript uses the following symbols to write valid code: $
, +
, =
, [
and ]
. To make this possible it requires jQuery UI to be present on the page.
Created by:
* Martin Kleppe @aemkei
* Sylvain Pollet-Villard @SylvainPV
With the symbols +
, [
and ]
it is possible to create the numbers from 0
to 9
.
0 === +[] 1 === ++[[]][+[]] 2 === ++[++[[]][+[]]][+[]]
We also can get undefined
by accessing the first element of an empty array. By concatenating that with an empty array, we get the String "undefined"
.
undefined === [][+[]] "undefined" === [][+[]] + []
Now we can access single characters from that string via their index.
`n` === "undefined"[1] `n` === ["undefined"][0][1] `n` === [[]+[][+[]]][+[]][++[[]][+[]]]
This gives the characters "f"
+"i"
+"n"
+"d"
and allows us to access a default jQuery method:
$.find === $["f"+"i"+"n"+"d"]
Functions can be transformed into their source code by converting them into a string via String(f)
, f+""
or f+[]
:
String($.find) === ($.find+"") ($.find+"") == ($.find+[])
The jQuery .find
method is quite complex because it contains the Sizzle selector library. In our case it is more than 3700 character. This allows us to get more characters:
console.log($.find+[]); "function Sizzle( selector, context, results, seed ) {...
With access to the alphabet we can combine characters to write our own code:
"alert(1)" === "a"+"l"+"e"+"r"+"t"+"("+"1"+")";
We need a way to execute the code. Unfortunately there is no direct access to the global scope to run eval()
. But the jQuery UI DatePicker plugin has a property dpDiv
that gives us access to the DOM:
document.body === $.datepicker.dpDiv[0].ownerDocument.bodyAccessing `window` would be possible, too using some `$.Widget` internals:
window === $.Widget._childConstructors[7]._proto.options.position.of
In the final step we inject a malicious image and assign the code to the onerror
handler:
document.body.innerHTML += '<img src onerror="alert(1)">';
This project is based on Martin Kleppe's JSFuck – a subset of JavaScript, where code is written using only six characters: []()!+
. For a long time the so called "Wall of Six" was considered not to be beaten.
Xchars.js by Sylvain Pollet-Villard was the first project that provided two different solutions for using five characters only: The [+=_]
subset required a script with a special ID. The second subset [+|>]
(initiated by Masato Kinugawa) makes use of the TC39 pipeline operator proposal which is currently only available as a Babel plugin.
The attack vector for the Xchars subset is small, because not many pages use script tags with single letter IDs and the pipeline operator is not yet implemented.
Note: The current version of $five is targeting only one specific version of jQuery UI (1.12.4) (because it maps specific characters based on the position in the source code) but the same principles work with other version. It uses a public property of the DatePicker plugin to get access to the document body and inject the obfuscated script.
# # ## # # ## # # ## # # # # # # # # ## # # # # # # # # # # # #20XX - Martin Kleppe