Why does (![]+[])[+!![]+[]] produce “a”

2019-02-02 00:23发布

问题:

I am interested in understanding the internals of JavaScript. I've tried to read the source for SpiderMonkey and Rhino but it's quite complex to wrap my head around.

The reason I ask is: why does something like

  • (![]+[])[+!![]+[]] produce "a"
  • (Å=[],[µ=!Å+Å][µ[È=++Å+Å+Å]+({}+Å)[Ç=!!Å+µ,ª=Ç[Å]+Ç[+!Å],Å]+ª])()[µ[Å]+µ[Å+Å]+Ç[È]+ª](Å) produce alert(1)?

Source: http://sla.ckers.org/forum/read.php?24,32930,page=1.

There's many more examples of JavaScript oddities on that forum and I wanted to know how it works from a programming point of view with respect to web application security.

回答1:

Why does (![]+[])[+!![]+[]] produce "a"

step by step: this is parsed in: (![]+[]) and [+!![]+[]]. First bit was already explained by artemb: [] is an array. Negating it, ![] evaluates to a boolean, false - that's just how ! works when it is applied to something that is not null or undefined. Again as pointed out by artemb, appending this +[] forces the boolean to be converted to a string. That's because the + is a string concatenation operator. The boolean false is then converted to its string representation, "false".

Then, the second bit, [+!![]+[]]. First of all, the outer [ and ] serve to treat the preceding string, which we just dervived is equal to "false" as an array of characters. By putting an integer index inside the [ and ], you get the character at a particular index. So what remains is +!![]+[] This consists of 4 pieces: +, !![], + and []. First !![] is evaluated. We already saw that ![] is a boolean false so prepending another ! negates it, and yields true. Next thing what happens is that the + in +!![] gets applied, and by applying + it converts the boolean true into the number representation, which is 1 (so +true is 1) The +[] that follows makes a string again from that 1 yielding "1" but it does not really make sense, the shorter expression (![]+[])[+!![]] already produces a. Appending +[] doesn't hurt either, the resulting expression is simply ["1"] instead of [1]. My hunch is that when [] is applied to an array, whatever is inside the [] will be coerced into a number, which for "1" would give 1 again. So either way, +!![]+[] evaluates to 1, making the final expression: "false"[1] which is saying: gimme the character at index 1 from the string "false", and since by default, arrays start at 0 in javascript, this is the second character of "false", and a.



回答2:

If you want to understand why those wierd expressions work as they do, you can open firebug console and experiment yourself. I did and I got that ![] is false, !![] is true, adding an array to a boolean value (false+[] or true+[]) produces a string-version of this value (false+[]="false").

That way the expression boils down to:

"false"["1"] 

which is obviously "a"



回答3:

Why does (![]+[])[+!![]+[]] produce "a"

  1. !expr - calls ToBoolean on expr and flips the boolean value. In other words, truthy values such as an empty array will produce false when used with the not operator.
  2. a + b - Both expressions are run through internal ToPrimitive. If either resulting value is a string, string concatenation is performed. Otherwise the primitives are run through ToNumber and added. ToPrimitive for objects (arrays included) will try toString and valueOf. Array.prototype.toString acts as calling join with no parameters. Thus, ![] + [] = false + "" = "false"
  3. !![] == true, the unary plus operator converts the expression to a number, so 1. Again, the array is converted to "" so +!![]+[] == "1".
  4. The expression boils down to ("false")["1"] == "a"

The other expression can be boiled down in a similar fashion. It uses unicode strings to mess it up and it's longer, but just as straight forward to "parse".



回答4:

I recommend you obtain and read:

  • ECMAScript standard (ECMA 262), 5th edition
  • Adobe document called "AVM 2 overview" which explains the architecture of the AVM2 virtual machine, on which Adobe Flash and its ActionScript run.