Hacker News new | past | comments | ask | show | jobs | submit login

It takes advantage of JS's complicated type coercion semantics and the overloading of the [] and + operators.

We start with [] and [[]].

convert [] to a boolean with !:

    ![] --> false
    !![] --> true
convert [] to undefined by subscripting it:

    [][[]] --> undefined
convert [] [[]] and true to numbers by prefixing them with + and adding them with +:

    +[]   --> 0
    +[[]] --> NaN
    +!![] --> +true --> 1
    +!![]+(+!![]) --> +true+(+true) --> 1+(1) --> 2
    etc.
convert any of these to strings by prefixing them with []+

    []+[]     --> ""
    []+![]    --> []+false     --> "false"
    []+[][[]] --> []+undefined --> "undefined"
    []+(+[])  --> []+0 --> "0"
get individual characters with the array subscript operator:

    ([]+![])[+[]] --> ([]+false)[0] --> "false"[0] --> "f"
    ([]+!![])[+!![]+(+!![])+(+!![])] --> ([]+true)[1+(1)+(1)] --> "true"[3] --> "e"
    etc...
So now we can obtain a limited number of characters:

    "a" "d" "e" "f" "i" "l" "n" "r" "s" "t" "u" "N" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
which we can combine into strings with the + operator:

    "f"+"i"+"l"+"t"+"e"+"r" --> "filter"
    "1"+"e"+"1"+"0"+"0"+"0" --> "1e1000"
It's not much, but it's enough to obtain large numbers:

    +("1"+"2"+"3"+"4") --> +("1234") --> 1234
    +("1"+"e"+"1"+"0"+"0"+"0") --> +("1e1000") --> Infinity
and, most importantly, to access a property of the array object:

    []["filter"]  --> function filter() { [native code] }
and by converting these back to a string:

    []+[]["filter"] --> "function filter() { [native code] }"
    []+Infinity     --> "Infinity"
we can expand our alphabet still further, and access some even more exciting properties:

    []["constructor"]           --> function Array() { [native code] }
    ([]+[])["constructor"]      --> function String() { [native code] }
    (![])["constructor"]        --> function Boolean() { [native code] }
    (+[])["constructor"]        --> function Number() { [native code] }
    []["filter"]["constructor"] --> function Function() { [native code] }
Almost there now.

By converting these back to strings we can access even more characters, and by passing the strings to the Function() constructor we can can construct functions and evaluate them! In other words, we have "eval". Let's use it to access the window object:

    []["filter"]["constructor"]("return this") --> function anonymous() {return this}
    []["filter"]["constructor"]("return this")() --> window
So now we have access to the global context and eval. We don't quite have access to the full range of letters, but we have enough letters to call toString, and use it's base conversion ability to get the full lowercase alphabet:

    10["toString"](36) --> "a"
    11["toString"](36) --> "b"
    ...
    25["toString"](36) --> "p"
    ...
    35["toString"](36) --> "z"
And now we have "p", we can use escape and unescape to get most of the rest:

    unescape(escape(" ")[0]+4+0) --> "@"
So there you have it!

The source code essentially runs this process backwards: it repeatedly uses regular expressions to convert the code back into "()[]!+" one step at a time.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: