Write-up: Origin confusion in Firefox via data: URL redirects

Archived article from 8 October 2012. Its content reflects the state of things at the time. Reconstructed from the web archive of secalert.net - view the original.

Summary. Firefox 15.0.1 handled window.location.assign() with a data: URL differently from WebKit browsers and Internet Explorer. After assigning a data: location that had been reached through a redirect, document.domain still held the redirecting site's origin, so script in the data: document could read that domain's cookies. The behaviour traces back to the HTML5 "Origin" rules for data: URLs returned by a redirect, and it affected Firefox only.

Background

The flaw is rooted in the HTML5 specification, section "5.3 Origin", which states: If a Document was generated from a data: URL that was returned as the location of an HTTP redirect (or equivalent in other protocols), the origin is an alias to the origin of the URL that redirected to the data: URL.

While researching DOM-based JavaScript behaviour across several browser engines, I found that newer versions of Firefox handled window.location.assign() differently from WebKit-based browsers and Internet Explorer. This opened a security flaw when the input was passed using the data: scheme. As a result, document.domain still held the value of the previously visited site after a new location had been assigned. In practice, this meant an attacker could read a domain's cookies on any inbound or outbound redirect, even one that was only ever meant to handle internal resources.

Example

Consider the following example code hosted on window.name.

location redirect example code

<script>

...

function getInternalURL(loc) {

   this.location.assign(loc);

}

...

</script>

A normal request would look like this:

usual redirect example
https://window.name/redirect.jsp?loc=https://window.name/

You would expect this to refer to the window object and reload the window with the supplied location as the new URI, and that is exactly what happens in this case.

Proof of concept

But what happens if we go a step further and try a different URI scheme?

proof of concept
... something like this:

https://window.name/redirect.jsp?InternalURL=data:text/html;base64,PGJvZHkgb25sb2FkPSJtYWtlRnJhbWUoKSI+PHNjcmlwdD5mdW5jdGlvbiBtYWtlRnJhbWUoKXtpZnJtPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIklGUkFNRSIpO2lmcm0uc2V0QXR0cmlidXRlKCJzcmMiLCJodHRwOi8vanNiaW4uY29tIik7aWZybS5zdHlsZS53aWR0aD02NDArInB4IjtpZnJtLnN0eWxlLmhlaWdodD00ODArInB4Ijtkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGlmcm0pO31hbGVydCgnbmFjaGhlcjonK3RoaXMuZG9jdW1lbnQuZG9tYWluKTthbGVydCh0aGlzLmRvY3VtZW50LmNvb2tpZSk7PC9zY3JpcHQ+

The redirect is triggered. Firefox then, surprisingly, reports "window.name" as this.document.domain and also discloses the cookies of window.name when accessed via this.document.cookie.

Why this matters

This is genuinely odd behaviour that only affects Firefox. If Firefox handled this the way WebKit browsers and Internet Explorer did, accessing document.domain would return something like "undefined" or "about:blank", which would be perfectly acceptable. Instead, Firefox kept this.document.domain set to the domain on which the redirect was triggered, which in this example is "window.name" rather than the expected "about:blank" or "undefined". An interesting edge case in how the specification was implemented.