Why Flash Security still matters?

Flash is still an active threat. In 2017, I reported Flash vulnerabilities to Facebook, Youtube, WordPress, Yahoo, Paypal and Stripe. Over the last 3 years, I reported more than 50 Flash vulnerabilities to Bug Bounty programs. And there are many more I didn’t have the time to report or that weren’t fixed after I reported it.



In addition, Flash has been replaced by new javascript/html5 features. These features introduce complexity and new kind of vulnerabilities like bad CORS implementation, DOM XSSes triggered by postMessage or XHR requests, active mixed content… Learning from Flash mistakes can help design and implement more secure javascript applications. The new Youtube html5 Api is mostly a porting of the Youtube Flash Api to javascript, making it interesting to study. In fact, I was able to find XSSes in the Youtube html5 Api using my knowledge of the Flash Api.

I’ll explain some advanced Flash vulnerabilities I found in Youtube Flash Api and in the process I will draw a parallel with html/javascript security. This is quite technical so feel free to comment or to tweet me (@opnsec) if something is not clear or if you want to add something. You can also read more about Flash security model here.

Reverse engineering Youtube Flash Api

Youtube Flash Api allows developers to embed a Youtube video in an external website.

Here is the workflow of the Api:

The entry point, Youtube Wrapper, is a Flash file located at youtube.com/v/[VIDEO_ID], it is just a wrapper between the HTML page and the Main App.

The Main Application is a large flash file of about 100k LoC and is located in a sandboxed domain s.ytimg.com.

The Modules handle optional functions like subtitles or advertisements. They are not stand-alone Flash files and can only be loaded by the Main App.

In addition there is a Flash to Javascript Api that allows the html page to send commands to the Youtube Api like play(), pause(), etc… Flash files will also perform “ajax style” cross-domain requests to load configuration files and video data.

I. User info leakage

Let’s start with a simple vulnerability. Here is a simplified version of Youtube Wrapper code in Flash ActionScript3 (AS3) :

public class YoutubeWrapper extends Sprite{ private var user_name = "The Victim";

private var user_picture = "https://googleusercontent.com/.../victim_photo.jpg";

private var appLoader = new Loader(); public function YoutubeWrapper(){

// allow external javascript/Flash files to access its public properties

Security.allowDomain("*");

// load the Main App

this.appLoader .load(new URLRequest("https://s.ytimg.com/.../watch_as3.swf");

// add as child of display container

this.addChild(this.appLoader );

// loaderInfo.sharedEvents Api

this.loader.contentLoaderInfo.sharedEvents

.addEventListener("REQUEST_USERINFO", this.onRequestUserinfo);

}

private function onRequestUserinfo(event:Event){

// write the user info into the event.data property

// which is accessible to the sharedEvents caller

event.data.user_name = this.user_name;

event.data.user_image = this.user_image;

}

}

Youtube Wrapper is generated on the fly and its property “user_name” contains the Google user name (if he is connected to Google). The property “user_picture” contains the link to the user profile picture. In this bug, the attacker will steal these values.

Youtube Wrapper can be loaded from the developer own Flash file (let’s call it Evil Wrapper). In that case, they both executes in a different Flash security sandbox.

Loading an external Flash file in Flash is a little bit similar to loading an <iframe> in html. If the iframe is from a different origin than its parent, they cannot access each other properties due to the Same-Origin Policy (SOP)

Youtube Wrapper contains the code Security.allowDomain(“*”) to allow javascript on the hosting webpage to send commands to the Flash app like play(), pause(),etc… This also means that Evil Wrapper can access any public property of Youtube Wrapper as if it was in the same Security sandbox. However it cannot access private properties.

The user_name property is private so Evil Wrapper cannot access it.

Flash also provides an Api for communication between a Loader and a loaded file using loaderInfo.sharedEvents. Youtube Wrapper uses this api to communicate with the Main App. When the Main App dispatches an event to the sharedEvents Api, the Youtube Wrapper receives the event and send back the user info using the event.data property.

loaderInfo.sharedEvents is not only accessible to the loader and the loaded files, but also to any Flash file that has a reference to this loaderInfo object.

This is similar to the javascript postMessage Api which allows communication between cross-domain iframes. The postMessage Api is accessible not only to the iframe and its parent but also to any other window that has a reference to the iframe or the parent. Any arbitrary domain can access these references using window.open and window.frames, which are not restricted by the SOP.

If Evil loader can access this particular loaderInfo object it will be able to send an event to the Youtube Wrapper and steal the user info.

loaderInfo is a property of appLoader which is a private property of the Youtube Wrapper, so Evil Wrapper cannot access it.

However, when using a Loader, if you want to display the loaded file, you have to add it as a child of the Display Container. This is usually done using

this.addChild(this.loader); and this is exactly what the Youtube Wrapper does.

The thing is that the Youtube Wrapper also have a built-in public method getChildAt() that will return the children of the Youtube Wrapper. This means that Evil Wrapper can call YoutubeWrapper.getChildAt(0) which will return the loader object, bypassing the privacy of the loader property.

Setting a property as “private” is called encapsulation. However, only the reference is private, not the object the reference points to.

From there Evil Wrapper can access YoutubeWrapper.getChildAt(0).loaderInfo.sharedEvents which is the interface between Youtube Wrapper and the Main App. Evil Wrapper can send an event to the Youtube Wrapper, the Youtube Wrapper will provide the user info in the event.data property and Evil Wrapper can then read the event.data value.

Proof of Concept :

Evil Wrapper code was like this

var loader = new Loader();

// Load the Youtube Wrapper

loader.load(new URLRequest("https://www.youtube.com/v/[VIDEO_ID]"));

var youtubeWrapper = loader.content;

// Access the Youtube Wrapper appLoader object

var appLoader = youtubeWrapper.getChildAt(0);

// Access the loaderInfo.sharedEvents of appLoader

var LeakingSharedEvents = appLoader.contentLoaderInfo.sharedEvents; // Prepare the event to send to Youtube Wrapper

var leakEvent = new Event("Request_username");

leakEvent.data = new Object();

// Send the leakEvent to the LeakingSharedEvents

LeakingSharedEvents.dispatchEvent(leakEvent); // The username is now accessible in the event.data property

trace(leakEvent.data.user_name);

trace(leakEvent.data.user_picture);

POC workflow :



Attack scenario:

Prerequisite: The victim is logged in Google and have Flash player

(1) The victim visits the attacker webpage evil.com/evil.html which contains a Flash object evil.com/evil.swf

(2) evil.swf loads Youtube wrapper (https://www.youtube.com/v/[VIDEO_ID]) and retrieves the user Google username (4-5-6)

evil.com now knows the username of it’s visitors. In addition, as the profile picture link is unique, it is possible to uniquely identify the user’s Google account.

Impact:

Any website could use this to know the identity of it’s users, if they are connected to Google. Imagine how you would feel if you visit a random website and this website displays your name and your picture!

Mitigation:

To resolve this issue, Youtube Wrapper stopped writing the user info in the event.data property and instead sends it directly to the Main App. That way even if Evil Wrapper sends an event to Youtube Wrapper, it wouldn’t receive the user info as it would be directly sent to the Main App.

Timeline:

08/27/2015 – reported to Google VRP

09/09/2015 – issue fixed and reward

This was a simple bug and I hope it helps you understand the basic challenges here. You can read about another bug where we actually execute arbitrary Flash code in youtube.com in Part 2.