0

I'm trying to solve a portswigger lab (https://portswigger.net/web-security/cross-site-scripting/contexts/lab-javascript-url-some-characters-blocked) and I can't understand why this code didn't pop up an alert when the expression window+'' is omitted:

x=x=>{onerror=alert; throw 1337},toString=x,window+''

A full explaination of this code snippet would be really appreciated, thanks.

xr0t625
  • 15
  • 2

1 Answers1

0

First of all, the code is using the comma operator (see also What does a comma do in JavaScript expressions?). What the comma operator does is allow you to evaluate multiple expressions. In particular in this case, you have three:

   x=x=>{onerror=alert; throw 1337},toString=x,window+''
// ^______________________________^ ^________^ ^_______^
//                 1                     2         3

Let's examine them one by one:

Brief note about implicit globals

This is going to be relevant for most of the below sections, so I want to clear it up first. Any time you assign to any name you haven't declared with var, let, or const it implicitly assigns to a property on the global object. In the browser, the global object is window, so any implicit globals go there:

console.log("before assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);

foo = 42;

console.log("after assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);

An arrow function

x=x=>{onerror=alert; throw 1337}

Definition

This will create a new arrow function and assign it to x. Since there is no variable declaration, x will be an implicit global and thus attached to window. This isn't extremely useful - it's likely done to save a few characters. The function also takes a single parameter called x but does nothing with it. Again, nothing useful - saves up a single character, otherwise it needs to be defined as ()=>.

Body part 1

What is more interesting is what that function does.

onerror=alert

First of all it assigns onerror to alert. Both of these will be the global error handler and the global alert() function passed as a reference. So, any time any error happens, it just shows an alert:

window.onerror = alert;

JSON.parse("{"); // parsing error

See What is the difference between a function call and function reference? and Difference of calling a function with and without parentheses in JavaScript for more information on how function references are used. In short, if you assign a function to a handler, then invoking that handler actually runs the function.

Body part 2

Then what happens is that the arrow function throws an error with throw 1337. In JavaScript, you can assign any value to be thrown, so it doesn't really matter whether it's an error or not. Throwing a number is thus valid. The value thrown isn't really relevant since the important thing is throwing an error. Due to the first part of the function, this throw statement will trigger the global error handler. Here is a demonstration (formatted):

x = x => {
  onerror = alert;
  throw 1337
}

x();

SO far, so good, but that's not how the function is invoked in the code. Let's go to the next part.

Overriding toString

toString=x

This part will override the toString method on window and change it tot he x function. Thus any time you explicitly or implicitly convert window to a string, it will instead execute x.

x = x => {
  onerror = alert;
  throw 1337
}

toString = x;

window.toString();

Converting window to string

In JavaScript, when you try to concatenate any value with a string, the value will be implicitly also be converted to a string. This is usually done by calling the toString() method:

const value1 = { "foo": 42 };
const value2 = { 
  "foo": 42,
  toString() {
    return `foo is ${this.foo}`;
  }
};

const value3 = { 
  "foo": 42,
  toString() {
    return "Hello world";
  }
};

console.log(value1 + ""); //default `toString()` method of objects
console.log(value2 + "");
console.log(value3 + "");

The same thing happens in the last part of the code:

window+''

This will:

  1. trigger conversion to a string, which
  2. calls the toString method on window, which
  3. was overridden with x using toString=x, which
  4. calls the x function instead, which
  5. sets the global error handler to just be alert (onerror=alert) and immediately throws an error (throw 1337`), which
  6. invokes the global error handler

Why omitting window+'' doesn't throw an error

Hopefully clear from the above, but to address it directly - that code is needed to set off the whole chain of reactions defined before it.

Here is the full code with explanation and formatting for clarity:

//create a function
window.x = x => {
  onerror = alert; //changes the global error handler
  throw 1337       //throws an error to trigger the error handler
};

//overwrite the `toString` method of `window` so it always throws an error
window.toString = window.x; 

//implicitly call `window.toString()` by performing a string concatenation
window + '';
VLAZ
  • 18,437
  • 8
  • 35
  • 54