Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
200 lines (184 sloc) 9.68 KB
---
layout: post
title: 'JSONP & handcrafted Flash files'
permalink: '/jsonp_handcrafted_flash_files/'
tags: ['jsonp', 'xss', 'flash']
---
<style>
.html { color: green; }
.http { color: orange; }
.js { color: blue; }
</style>
<div class="lead">
<p>
I gave a talk at <a href="http://2013.appsec-forum.ch/" class="external">ASFWS 2013</a> about JSONP security. The purpose of my talk was
to share some knowledge related to attacking and securing JSONP endpoints.
</p>
<p>
The talk started with an overview of JSONP, followed by a combination of well known and lesser known security issues.
</p>
<p>
I then talked about crafting Flash files containing only specific bytes. The purpose of such files is to CSRF the domain
hosting the JSONP endpoint by injecting the Flash code as the callback parameter.
</p>
<p>
These are my notes I gathered while preparing the talk. Credits to <a href="https://www.facebook.com/erling" class="external">Erling</a> for exploring these issues with me and debugging my work.
</p>
</div>
<section>
<div class="page-header"><h3>A protocol to talk cross domain</h3></div>
<ul>
<li>Browsers implement a policy called "Same Origin Policy". This policy prevents different domains from stealing each others data.</li>
<li>JSONP is a protocol (actually, more of a convention) to bypass the Same Origin Policy in order to exchange specific pieces of information.</li>
<li>Sample application: api.pizza.com, a website which lets users share their favorite pizza toppings across different web sites.</li>
<li>Data consumer (http://some-site.com/):
<pre><span class="html">&lt;html&gt;
&lt;body&gt;
...
&lt;script&gt;</span>
<span class="js">function onTopping(data) {
...
}</span>
<span class="html">&lt;/script&gt;
...
&lt;script src="http://api.pizza.com/?callback=onTopping"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</span></pre>
</li>
<li>Data provider (http://api.pizza.com/?callback=onTopping):
<pre><span class="http">HTTP/1.1 200 OK
Date: Mon, 07 Oct 2013 20:45:39 GMT
Server: Apache</span>
<span class="js">onTopping({... json encoded data ...})</span>
</pre>
</li>
<li>The callback convention allows people to mash different APIs without having collisions.
</ul>
</section>
<section>
<div class="page-header"><h3>Types of data providers</h3></div>
<p>There are three types of data providers: stateless, token based and cookie based.</p>
<ul>
<li>Stateless APIs are useful for querying public information. E.g. a weather api.</li>
<li>Token based APIs require some kind of protocol to exchange the token. E.g. Connect or OAUTH.</li>
<li>Stateless and token based APIs should be hosted on different domains. E.g. api-pizza.com.</li>
<li>Cookie based APIs should use a subdomain. E.g. api.pizza.com.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>Trusting api.pizza.com</h3></div>
<ul>
<li>Malicious data providers can XSS the data consumer.</li>
<li>Data consumer can protect themselves by using a combination of iframe + different domain.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>Content-Type header</h3></div>
<ul>
<li>If the callback's value is "&lt;html&gt;&lt;body&gt;&lt;script&gt;...", a data provider can be XSSed.</li>
<li>Data providers can protected themselves by sending a <code>Content-Type: application/json</code> header.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>X-Content-Type header</h3></div>
<ul>
<li>In some cases, a browser can be tricked to "sniff" the content type, even when a <code>Content-Type</code> header was sent.</li>
<li>The data provider should therefore also send <code>X-Content-Type: nosniff</code>.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>Injecting Flash files</h3></div>
<div>For the following reasons, injecting a Flash file in the callback can lead to a CSRF.</div>
<ul>
<li><code>&lt;object type="application/x-shockwave-flash"...&gt;</code> can be used to bypass <code>X-Content-Type</code>.</li>
<li>Flash files are allowed to talk back to the originating domain. The exact policy is controlled by the <code>crossdomain.xml</code> file.</li>
<li>From a web security point of view, hosting Flash files is not equivalent to hosting JavaScript.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>SWF file format</h3></div>
<p>The SWF file format specification is not strictly enforced by current Flash players.</p>
<ul>
<li>Version, FileLength, FrameSize, FrameRate and FrameCount fields are ignored when invalid.</li>
<li>An invalid doAction code is ignored (equivalent to a noop).</li>
</ul>
</section>
<section>
<div class="page-header"><h3>Multiple ways to compress a SWF file</h3></div>
<ul>
<li>FWS, not compressed. We end up always having a null byte. (0x00-0x7f?)</li>
<li>CWS with BTYPE=00, compression with no compression (mind blowning!).</li>
<li>CWS with BTYPE=01, compression with fixed Huffman codes.</li>
<li>CWS with BTYPE=10, compression with dynamic Huffman codes.</li>
<li>CWS with BTYPE=11, unknown/invalid compression?</li>
<li>ZWS, compression with LZMA.</li>
</ul>
</section>
<section>
<div class="page-header"><h3>Custom compressed data streams</h3></div>
<p>Most compression algorithms generate "instructions", which the decompression algorithm interprets. The compression
process can therefore make specific choices.</p>
<p>Let's imagine a run-length compression system. The string "AAAA" can be compressed as "4A", "2A,2A", "A,3A", etc. "4A" is
obviously the most efficient compression, but the other options are valid too.</p>
<p>In general, compression algorithms are designed to create the smallest possible stream. We can however compress data in
a way that only emits specific bytes.</p>
<p>As an example, here is valid Flash file which only uses bytes in the 0x03-0x7e range:</p>
<pre>0000000: 4357 536a 6163 6b69 6843 5254 5464 6060 CWSjackihCRTTd``
0000010: 6030 6006 681a 3b03 437c 517e 7e09 0340 `0`.h.;.C|Q~~..@
0000020: 323e 2e7e 3e3e 4911 4060 3c0b 3046 0606 2>.~>>I.@`<.0F..
0000030: 0303 0606 0606 0606 0606 2f61 316f 6b06 ........../a1ok.
0000040: 0606 0606 0606 0706 0606 404e 0b09 12 ..........@N...</pre>
</section>
<section>
<div class="page-header"><h3>Conclusion</h3></div>
<ul>
<li>Host JSONP api on different domain.</li>
<li>Filter callback. <code>/^[a-zA-Z0-9]{1,32}$/</code> seems reasonable, but cannot be guaranteed to be safe.</li>
<li>An empty JavaScript comment breaks the Flash injection attack.</li>
<li>Be careful with PDF injection!</li>
<li>Use CORS when possible.</li>
</ul>
<pre><span class="http">HTTP/1.1 200 OK
Date: Mon, 07 Oct 2013 20:45:39 GMT
Server: Apache
Content-Type: application/json
X-Content-Type: nosniff</span>
<span class="js">/**/onTopping({... json encoded data ...})</span>
</pre>
</section>
<section>
<div class="page-header"><h3>Future work?</h3></div>
<ul>
<li>Using doABC instead of doAction bytecodes.</li>
<li>ZWS.</li>
<li>CWS with dynamic Huffman codes.</li>
<li>Crafting Flash files with a genetic algorithm.</li>
<li>Can a CSRF on api.pizza.com escalte to an attack on pizza.com or www.pizza.com?</li>
</ul>
</section>
<section>
<h3>Links</h3>
<ul>
<li><a href="http://2013.appsec-forum.ch/" class="external">AppSecForum 2013</a></li>
<li><a href="http://en.wikipedia.org/wiki/JSONP" class="external">Wikipedia page on JSONP</a></li>
<li><a href="http://web.archive.org/web/20060212113746/http://htmatters.net/htm/1/2005/07/evaling-JSON.cfm" class="external">George Jempty's proposal for JSON++ (July 2005)</a></li>
<li><a href="http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/" class="external">Bob Ippolito's propsal for JSONP (December 2005)</a></li>
<li><a href="https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_cookies" class="external">Same origin policy for cookies</a></li>
<li><a href="https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_Flash" class="external">Same origin policy for Flash</a></li>
<li><a href="http://security.stackexchange.com/questions/23438/security-risks-with-jsonp" class="external">Security risks with JSONP? (October 2012)</a></li>
<li><a href="http://beebole.com/blog/general/sandbox-your-cross-domain-jsonp-to-improve-mashup-security/" class="external">Sandbox Your Cross Domain JSONP To Improve Mashup Security</a></li>
<li><a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf-file-format-spec.pdf" class="external">SWF file format specification (version 19)</a></li>
<li><a href="http://www.ietf.org/rfc/rfc1951.txt" class="external">Deflate</a></li>
<li><a href="http://www.ietf.org/rfc/rfc1952.txt" class="external">GZIP</a></li>
<li>cure53.de xmas2013 write up: <a href="https://cure53.de/xmas2013/writeup">Who controls the first bytes controls the SOP</a></li>
<li>Got a <a href="https://bounty.github.com/researchers/alokmenghrajani.html">bounty</a> too for this bug, thank you github.com!</li>
<li>
Twitter is kind of vulnerable to this issue. I reported it to them in Oct 2013. 5 months later they got back to me
with: "We do not plan to make any changes to our JSON APIs at this time.".
</li>
<li>About a year after I posted my findings, Gábor and Michele improved on my work. They coined the term Rosetta Flash for this attack.
<a href="https://molnarg.github.io/ascii-flash/" class="external">Gábor Molnár's slides</a>,
<a href="http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/" class="external">Michele Spagnuolo's post</a>
</li>
</ul>
</section>