Alok Menghrajani

Previously: security engineer at Square, co-author of HackLang, put the 's' in https at Facebook. Maker of CTFs.


This blog does not use any tracking cookies and does not serve any ads. Enjoy your anonymity; I have no idea who you are, where you came from, and where you are headed to. Let's dream of an Internet from times past.


Home | Contact me | Github | RSS feed | Consulting services | Tools & games

I gave a talk at ASFWS 2013 about JSONP security. The purpose of my talk was to share some knowledge related to attacking and securing JSONP endpoints.

The talk started with an overview of JSONP, followed by a combination of well known and lesser known security issues.

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.

These are my notes I gathered while preparing the talk. Credits to Erling for exploring these issues with me and debugging my work.

  • Browsers implement a policy called "Same Origin Policy". This policy prevents different domains from stealing each others data.
  • JSONP is a protocol (actually, more of a convention) to bypass the Same Origin Policy in order to exchange specific pieces of information.
  • Sample application: api.pizza.com, a website which lets users share their favorite pizza toppings across different web sites.
  • Data consumer (http://some-site.com/):
    <html>
      <body>
        ...
        <script>
          function onTopping(data) {
            ...
          }
        </script>
        ...
        <script src="http://api.pizza.com/?callback=onTopping"></script>
      </body>
    </html>
  • Data provider (http://api.pizza.com/?callback=onTopping):
    HTTP/1.1 200 OK
    Date: Mon, 07 Oct 2013 20:45:39 GMT
    Server: Apache
    
    onTopping({... json encoded data ...})
    
  • The callback convention allows people to mash different APIs without having collisions.

There are three types of data providers: stateless, token based and cookie based.

  • Stateless APIs are useful for querying public information. E.g. a weather api.
  • Token based APIs require some kind of protocol to exchange the token. E.g. Connect or OAUTH.
  • Stateless and token based APIs should be hosted on different domains. E.g. api-pizza.com.
  • Cookie based APIs should use a subdomain. E.g. api.pizza.com.
  • Malicious data providers can XSS the data consumer.
  • Data consumer can protect themselves by using a combination of iframe + different domain.
  • If the callback's value is "<html><body><script>...", a data provider can be XSSed.
  • Data providers can protected themselves by sending a Content-Type: application/json header.
  • In some cases, a browser can be tricked to "sniff" the content type, even when a Content-Type header was sent.
  • The data provider should therefore also send X-Content-Type: nosniff.
For the following reasons, injecting a Flash file in the callback can lead to a CSRF.
  • <object type="application/x-shockwave-flash"...> can be used to bypass X-Content-Type.
  • Flash files are allowed to talk back to the originating domain. The exact policy is controlled by the crossdomain.xml file.
  • From a web security point of view, hosting Flash files is not equivalent to hosting JavaScript.

The SWF file format specification is not strictly enforced by current Flash players.

  • Version, FileLength, FrameSize, FrameRate and FrameCount fields are ignored when invalid.
  • An invalid doAction code is ignored (equivalent to a noop).
  • FWS, not compressed. We end up always having a null byte. (0x00-0x7f?)
  • CWS with BTYPE=00, compression with no compression (mind blowning!).
  • CWS with BTYPE=01, compression with fixed Huffman codes.
  • CWS with BTYPE=10, compression with dynamic Huffman codes.
  • CWS with BTYPE=11, unknown/invalid compression?
  • ZWS, compression with LZMA.

Most compression algorithms generate "instructions", which the decompression algorithm interprets. The compression process can therefore make specific choices.

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.

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.

As an example, here is valid Flash file which only uses bytes in the 0x03-0x7e range:

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...
  • Host JSONP api on different domain.
  • Filter callback. /^[a-zA-Z0-9]{1,32}$/ seems reasonable, but cannot be guaranteed to be safe.
  • An empty JavaScript comment breaks the Flash injection attack.
  • Be careful with PDF injection!
  • Use CORS when possible.
HTTP/1.1 200 OK
Date: Mon, 07 Oct 2013 20:45:39 GMT
Server: Apache
Content-Type: application/json
X-Content-Type: nosniff

/**/onTopping({... json encoded data ...})
  • Using doABC instead of doAction bytecodes.
  • ZWS.
  • CWS with dynamic Huffman codes.
  • Crafting Flash files with a genetic algorithm.
  • Can a CSRF on api.pizza.com escalte to an attack on pizza.com or www.pizza.com?

Links