译者:興趣使然的小胃
预估稿费:180RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
一、前言
Flash至今仍是一个活跃的威胁来源。在2017年,我分别向Facebook、Youtube、WordPress、Yahoo、Paypal以及Stripe提交了Flash漏洞。在过去的3年中,我向漏洞奖励项目提交了超过50个Flash漏洞,获得了超过80k美元的奖励。由于精力有限,还有很多漏洞我来不及提交,也有些漏洞在我提交后还没有被修复。
与此同时,Flash已经被新推出的javascript/html5所取代。这些功能引入了新的更为复杂的漏洞,比如错误的CORS实现方法、通过postMessage或XHR请求触发DOM XSS、主动混淆的数据内容等等。我们可以从Flash漏洞中汲取教训,以实现更加安全的javascript应用程序。新推出的Youtube html5 Api看上去更像是Youtube Flash Api移植到javascript上的实现版本,因此是一个非常理想的研究对象。实际上,利用我在Flash Api中积累的经验,我的确在Youtube html5 Api中找到了多个XSS漏洞。
在本文中,我会介绍我在Youtube Flash Api中找到的一些高级Flash漏洞,同时我也会顺便提到html/javascript安全性方面的内容。这些内容的技术细节比较多,所以如果有什么意见或者建议可以随时通过Twitter联系我(@opnsec)。你也可以访问此链接以了解Flash安全模型的详细信息。
二、逆向分析Youtube Flash Api
开发者可以利用Youtube Flash Api在外部网站中插入Youtube视频。
Api的工作流程如下图所示:
作为入口点,Youtube Wrapper其实是一个Flash文件,文件的具体路径为youtube.com/v/[VIDEO_ID],该文件其实是HTML页面以及Main App(主应用)之间的一个封装器。
主应用(Main Application)是一个大型Flash文件,大约有100k行代码,地址位于s.ytimg.com这个沙箱域名中。
Modules(模块)用来处理可选功能,比如字幕或者广告功能。这些模块不是独立的Flash文件,只能由Main App来加载。
此外,还有一个Flash转Javascript的Api,html页面可以通过这个Api向Youtube Api发送命令,这些命令包括play()、pause()等等。Flash文件也会执行ajax风格(ajax style)的跨站请求,以加载配置文件以及视频数据。
三、用户信息泄露漏洞
首先,我们先来分析一个简单的漏洞。如下这段Flash ActionScript3(AS3)代码是一段简化版的Youtube Wrapper实现代码:
这段代码会实时生成Youtube Wrapper,其中“username”属性包含Google用户的用户名(如果用户已连接到Google),“userpicture”包含用户个人资料图片的具体链接。在这个漏洞中,攻击者可以窃取这些属性的值。
开发者可以从自己的Flash文件(我们可以称之为Evil Wrapper)中加载Youtube Wrapper。在这种情况下,这两种Wrapper都会在另一个Flash安全沙箱中执行。
备注:
在Flash中加载外部Flash文件有点类似于在html中加载一个<iframe>。如果iframe的来源与其父节点不同,由于同源策略(Same-Origin Policy,SOP),这两者无法访问彼此的属性。
Youtube Wrapper中包含Security.allowDomain("*")这样的代码,以便宿主网页中的javascript能够向Flash应用发送诸如play()、pause()等命令。这也意味着Evil Wrapper可以访问Youtube Wrapper的任何公开属性,因为这两者处于同一个安全沙箱中。然而,Evil Wrapper无法访问私有属性。
由于user_name属性为私有属性,因此Evil Wrapper无法访问它。
此外,Flash还提供了一个Api以便Loader(加载者)与被加载文件之间能够通过loaderInfo.sharedEvents相互通信。Youtube Wrapper使用这个api来与Main App进行通信。当Main App将一个事件(event)发送给sharedEvents Api时,Youtube Wrapper会收到这个事件,然后利用event.data这个属性来返回用户信息。不仅Loader以及被加载文件能够访问loaderInfo.sharedEvents,只要引用了这个loaderInfo对象,其他任何Flash文件都可以访问loaderInfo.sharedEvents。
备注:
这种情况类似于javascript的postMessage Api,这个Api同样可以允许跨域iframe之间的通信交流。不仅iframe及其父窗口可以访问postMessage Api,只要使用window.open以及window.frames,任何域名都可以访问这些引用,这种情况不受SOP限制。
如果Evil Loader可以访问这个loaderInfo对象,那么它就可以向Youtube Wrapper发送事件,窃取用户信息。
由于loaderInfo是appLoader的一个属性,而appLoader是Youtube Wrapper的一个私有属性,因此Evil Wrapper无法访问这个属性。
然而,当使用Loader时,如果我们想要显示已加载的文件,就必须将其添加为Display Container的子节点。通常情况下,我们会使用this.addChild(this.loader)来完成这个任务,这也正是Youtube Wrapper采用的方法。
问题在于,Youtube Wrapper还有一个内置的公共方法,名为getChildAt(),这个方法可以返回Youtube Wrapper的子节点。这意味着Evil Wrapper可以调用YoutubeWrapper.getChildAt(0),这样就可以返回Loader对象,绕过Loader属性的隐私限制策略。
备注:
将属性设为“私有(private)”是一种封装方法。然而,这种情况下,只有引用本身是私有的,引用所指向的那个对象并不是私有对象。
这样一来,Evil Wrapper就可以访问YoutubeWrapper.getChildAt(0).loaderInfo.sharedEvents,而这正是Youtube Wrapper以及Main App之间的接口。Evil Wrapper可以往Youtube Wrapper发送事件,Youtube Wrapper会在event.data属性中提供用户信息,因此Evil Wrapper就可以从event.data中读取用户信息。
3.1 PoC代码
Evil Wrapper代码如下所示:
3.2 PoC工作流程
PoC代码的工作流程如下图所示:
3.3 攻击场景
攻击的前提是,受害者的Google处于已登录状态,并且受害者已安装Flash player。
攻击场景如下:
(1)受害者访问攻击者的网站(evil.com/evil.html),这个网站包含一个恶意的Flash对象(evil.com/evil.swf)。
(2)evil.swf加载Youtube wrapper(https://www.youtube.com/v/[VIDEO_ID]),获取用户的Google用户名(4-5-6),然后evil.com就可以得知访问该站点的用户名。此外,由于个人资料的图片链接具有唯一特征,因此攻击者有可能识别出用户的Google账户。
3.4 影响范围
如果网站用户已登录Google,那么任何网站都可以使用这种方法来提取网站访问用户的身份。想象一下,如果我们随机访问一个网站,然后这个网站竟然会显示我们的姓名和照片,惊不惊喜?意不意外?
3.5 缓解措施
为了解决这个问题,Youtube Wrapper已经停止往event.data属性中写入用户信息,现在会将用户信息直接发往Main App。这样处理后,即使Evil Wrapper往Youtube Wrapper发送消息,它也不会收到用户信息,因为相关信息会直接发送到Main App。
3.6 时间线
08/27/2015 – 向Google VRP提交漏洞
09/09/2015 – 漏洞被修复,获得奖励(1星,满分4星)。
四、总结
这是一个简单的漏洞,希望你能通过这个漏洞理解基本的原理。你可以进一步阅读第二篇文章,在那篇文章中,我们利用另一个漏洞实现了在youtube.com上执行任意Flash代码。