How did the Facebook Originull vulnerablity of Access-Control-Allow-Origin: null allow cross-origin access?
I'm Ysrael and I'm the researcher that found this vulnerability.
Let's divide your question into 2 parts:
A. How did the Origin of the browser become null
?
B. How the null
Origin affect Facebook servers?
Let's start with A.
The Origin is part of the CORS mechanism and it is intended to tell the server where the request comes from.
When the server gets the request, the server can decide to allow this Origin to receive the response.
One of the ways to bypass this protection was to find an Open-Redirect
within one of the pages, and then to direct the user to dataURI schema.
In the past, dataURI received the previous Origin because there was no other Origin to give him.
As a security improvement, the modern browser sets the Origin to null
so the XHR requests from this dataURI page won't have the proper Origin.
In Chrome, this is the situation in any case, and in Firefox it happens only when the page refreshed via meta tag to dataURI.
So this is how the Origin became null
.
The 'Originull' attack uses this behavior in order to bypass security checks that are based on the Origin, because most of the time, the programmers won't think of null
as the value in this field.
Now let's get to part B. How this affected Facebook.
Facebook uses Access-Control-Allow-Origin
on its Messenger servers, because they use a different sub-domain.
Most likely, the Messenger sub-domain has a security filter on the entrance, and then the requests are passed to the internal server that responds to the users.
When the server wanted to respond, it took the value from the request's Origin and returned it on the Access-Control-Allow-Origin
header.
This development design allows Facebook to keep up the allowed Origins in one place only.
The messenger sub-domain also allows regular GET requests.
Regular GET requests didn't have an Origin header at all.
In many development languages, a non-exist Header returns the value null
.
So, the filter has a condition that allows null
to pass (so the GET requests will pass correctly), and the server takes the value that it receives in the request's Origin header (null
) and returns it to the client in the Access-Control-Allow-Origin
header.
Simple, right? ;)
You can find the technical details and screen shots in the full-disclosure document on BusSec's web site at: https://www.bugsec.com/wp-content/uploads/2016/12/Blog-Post-BugSec-Cynet-Facebook-Originull.pdf
It's possible to exploit Access-Control-Allow-Origin: null
because resources loaded over things like data URI's and sandboxed iframes use the null
origin. A PDF documenting the exploit confirms that Originull used a data URI document to exploit this header to achieve cross-origin access.
For this reason, the W3C recommends avoiding returning this value for the header.
7.4. Avoid returning
Access-Control-Allow-Origin: "null"
It may seem safe to return
Access-Control-Allow-Origin: "null"
, but the serialization of the Origin of any resource that uses a non-hierarchical scheme (such asdata:
orfile:
) and sandboxed documents is defined to be "null". Many User Agents will grant such documents access to a response with anAccess-Control-Allow-Origin: "null"
header, and any origin can create a hostile document with a "null" Origin. The "null" value for the ACAO header should therefore be avoided.The simple string comparison of CORS as applied to "null" is controversial. Some believe that "null" should be treated as a keyword token indicating the lack of an Origin, which, when tested, should never compare as equal to another "null" Origin. (As is the case with
null
values in SQL, for example.) It is unwise to build systems which rely on the "null" equals "null" comparison as this behavior may change in the future.
So as it turns out, a value of null
currently means nothing special. It's not the same as *
, it's just a not-so-special origin which can currently match against origin-less resources.
So if you return this header (even by accident):
Access-Control-Allow-Origin: null
Your resources will currently be accessible cross-origin.