1

The main reason, to my understanding, that Flash is being phased out is that it is fundamentally insecure (i.e. it isn't just tons of individual issues; it's insecure on a 'core' level). I severely doubt that anyone would knowingly build something so insecure (so they wouldn't just accept massive security issues even in favour of program flexibility). This leads me wonder, how can we analyze Flash's shortcomings in order to prevent the same underlying problem from occurring?

Is there a particular form of analysis that can address this issue?

1

3 Answers 3

13

I disagree with Michael's reply. Sandboxing based on blacklisting or whitelisting does work if you implement it correctly, as then there is no way that running code can ever circumvent it. Otherwise sandboxing as a whole would be impossible since even sandboxing in OS kernels uses black-/whitelisting, yet as the kernel enforces the rules, a user-space app has no chance to bypass any of the restrictions, unless it can manipulate the kernel and if it can do that, you have much more serious problems than apps bypassing sandboxing as then any app can take over your entire system.

Also, the security problems were not caused by the fact that the browser didn't put plugins into a sandbox but by the Flash player itself. The browser also doesn't put JavaScript engines into a sandbox or WebAssembly runtime; both have full access to the entire system, just the code they execute has limited access and the same would have been true for the Flash code that the Flash player executes. Correctly implemented Flash would be as secure as any other existing Web technique that can execute code loaded from remote servers. A sandbox would just have been another layer of security, basically a safety net, and when do you need a safety net? Correct, you only need one if the actual security mechanism fails. A safety net should never be your main source of security. Allowing code to run wild and relying that a proper sandbox will prevent it from doing anything evil is a horrible security concept.

The main problem of Flash was that it wasn't designed with security in mind. If security is an issue, you need to think about it before you write your first line of code. Yet Flash had a gigantic code base that was written without investing any time to think about security as it wasn't originally written for the web. It started off as a vector drawing app from FutureWave named SmartSketch (1993-1995), that later on offered vector-based, interactive animations. Being a locally executed app, security was not an issue. Only later the idea was born, that these animations could also run inside a browser using a browser plugin. As this was competing with Shockwave from Macromedia (a similar concept, yet focused on bitmap animations, sound, and video), Macromedia obtained SmartSketch from FutureWave, added some of its Shockwave functionality to it and Shockwave Flash was born (1995); this name was later truncated to just Flash.

Initially Flash wasn't even able to run real program code. It was just a data format that described which content is displayed/played, how it is displayed/played, described animation paths and transformations, and reactions to timed or interactive events. This was cool for graphical animations, interactive navigation menus, or very simple apps and games, yet pretty soon Macromedia realized that for more complex uses cases, they require a real, fully-featured programming language. So in 2000, with the release of Flash 5, something new was introduced: ActionScript. The language was similar to JavaScript with some concepts borrowed from HyperTalk and was later on (version 2) extended with concepts and syntactical sugar that made it also similar to Java. And with version 3 a JIT compiler was written, that would convert AS to native code to greatly enhance performance.

The problem was that any security concept that Flash offered wasn't part of its core but was later on put over the whole thing and it had holes like cheese. With JIT-compiled code that focused on performance and not on security, it became possible for attackers to intentionally produce stack overflows and then be able to call arbitrary native code of the system, breaking out of the AS sandbox. Also AS allowed direct interaction with many native functions of the Flash player to control animations or access audio hardware. Many of them were predating AS and were simply not prepared for the fact, that someone may intentionally feed wrong data into these functions as when called by the player itself, this would never happen, so sanity and type checks were often missing for performance reasons. And there were thousands of these. By feeding bad/broken/incorrect data into these functions, again, stack overflows could be produced and arbitrary code could be executed or arbitrary functions could be called. Fixing all of them was a whack-a-mole game.

If we learn something from Flash, it is that you will have a very hard time to retrospectively turn an insecure system that was never designed with security in mind into a secure system by trying to put security concepts on top of it. If security is really the top priority, throwing everything away and starting from scratch will be faster and lead to better results. Yet, of course, Adobe, who bought Macromedia, had no interest in doing so. They just wanted to make maximum profit out of their 3.4 billion dollar investment they had to pay to buy a bunch of security-wise rotten systems, that were now maintained by new owners which didn't even know half of the dirty code paths still in the system since day one.

1
  • +1 TLDR; You need to design and implement security as you add each feature/layer. Trying to go back after the fact and apply security is almost impossible for something of this level of complexity.
    – JimmyJames
    Apr 22, 2020 at 17:18
6

It's not just Flash; the Java browser plugin had exactly the same issues, and the picture that emerges is pretty clear:

Sandboxing based on blacklists or even whitelists of functions of a general API does not work.

That was the heart of the problem: the old Netscape Browser Plugin API allowed plugins to be regular executables with full access to the local machine, and the Java and Flash plugins had sandboxes with complex APIs to allow pretty much full access to the local machine, but with the "dangerous parts" disabled by default. Except people found endless ways to circumvent these restrictions or use the "harmless" parts to do dangerous things.

If you have a sandbox in which you're going to execute potentially malicious code, it must not have an API to access arbitrary files, or access the clipboard directly, or run shell commands. Its API to the outside should be as small, simple, and restrictive as possible.

As an extension to this: if you're a browser or similar application, your plugin API will be used to implement sandboxes for running potentially malicious code. So it should follow the above guideline as well.

0

Lack of adversarial testing.

Any system, particularly those that have explicit support for foreign code execution, exposes an API.

That API is at least powerful enough to do something useful and meaningful. In essence it has power, and with power comes responsibility.

Generally API's are built to live in a peaceful suburbia where no one is out to get them. Here all of the examples play nice, even those looking for a negative outcome are playing the nice game of catch this specific case. Which is why they break so easily when someone even simply makes a mistake, let alone has it out to be mischievous.

The lesson is to have a code driver that does not play by the rules. It intentionally chooses actions to see what will happen, perhaps even looking at the code to find the edge cases. Its a game:

  • of capture the flag. Intentionally trying to extract "secret" information. Even better if repeating the sequence of steps has a high probability of working.
  • of musical chairs. Intentionally force something in the larger program to become corrupted: data structures, the stack, code.
  • of tripping. Force the process to generate an unexpected error: Out of memory, divide by zero, process crash, etc...

Each sequence of steps is essentially a test, perhaps needing optimisation to find the shortest set of steps to cause the issue.

Chances are that several kinds of adversarial driver will be needed, including a human.

I include a static code analyser that highlights issues as an adversarial code driver.

5
  • Half of the Flash security problems completely circumvented the API, e.g. by abusing flaws in the JIT compiler, which is not an API, as the code is never directly interacting with the JIT compiler.
    – Mecki
    Apr 22, 2020 at 15:38
  • How is the JIT compiler not an API? it is the service provider of code compilation for the language. That is a powerful behaviour, even though it isn't the platform API in the sense of libraries provided for use.
    – Kain0_0
    Apr 23, 2020 at 0:21
  • 1
    Code is not interacting with a JIT compiler. When your code is executed, it won't even know if it is purely interpreted, JIT compiled, or AOT compiled at start. An API is an interface that offers functionality to your code and that your code must actively use to make something happen, a JIT compiler works secretly in the background and is entirely transparent to the code it executes. Yet, if your code can assume it is JIT compiled and it knows about weaknesses in the JIT compiler, it can still abuse that to do things like breaking out of its sandbox.
    – Mecki
    Apr 23, 2020 at 9:06
  • @Mecki. I understand that, but Code itself is an application programming interface. It is the interface to the interpreter/cpu and a jit is an adapter. That means that certain arrangements of code can cause beneficial/malevolent misbehaviours. I do accept that for some reason this isn't the prevalent view in the community. Perhaps because we are used to thinking of APIs as being used imperatively as opposed to being used declaratively.
    – Kain0_0
    Apr 30, 2020 at 22:50
  • You can of course make up your own definition of the term API and then everything you like will be covered by it but the accepted definition is: An application programming interface (API) is a computing interface which defines interactions between multiple software intermediaries. And this doesn't apply to a CPU and also not to a virtual machine that emulates a CPU in software.
    – Mecki
    May 2, 2020 at 0:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.