Bad regex used in Facebook Javascript SDK leads to account takeovers in websites that included it
This bug could allow an attacker to target websites that included Facebook Javascript SDK. The attacker would trick a certain website user to visit his website which would exploit a bug in the implementation used in the SDK to allow cross-origin communication between the website that included the SDK, and Facebook main website. The Facebook JS SDK is widely used by developers for various reasons like logging-in to the website using Facebook or to embed Share or Like plugins which left thousands of websites vulnerable to various attacks like account takeovers.
Technical Details
The Facebook JS SDK could be implemented in a website for various reasons like to Login with Facebook, Sharing, Embedding. Also the same SDK could is used to communicate with Facebook website to authenticate and serve content for Canvas/Tab applications that would be accessed via apps.facebook.com or page tabs. This is done by including the SDK in a page in your website which would have the content for the canvas application, then you would add the link of the page in your Facebook canvas application settings. If you visit apps.facebook.com/APP_ID/, you should find that the page selected would be served in an iframe and Facebook website would communicate with your website and vice versa by using postMessage method and EventListeners for cross-origin window messages.
The main key for secure cross-origin communication is to always verify the origin and destination of a certain message. Facebook failed to do that in the SDK which is included in a third party website because it was using a wrong regular expression to verify the origin of a certain cross-origin message received :
This is a snippet of the code responsible for cross-origin communication in the SDK:
__d("sdk.XD", ["JSSDKXDConfig", "Log", "QueryString", "Queue", "UrlMap", "guid", "isFacebookURI", "resolveWindow", "sdk.Event", "sdk.feature", "sdk.RPC", "sdk.Runtime", "sdk.Scribe", "sdk.URI"], (function(a, b, c, d, e, f) {
var g = new(b("Queue"))(),
h = "parent",
i = null,
j = /^https:\/\/.*facebook.com$/;
...
window.addEventListener("message", function(a) {
var c = a.data,
d = a.origin || "native";
if (!/^(https?:\/\/|native$)/.test(d)) {
b("Log").debug("Received message from invalid origin type: %s", d);
return
}
if (!j.test(d)) return;
if (typeof c === "string") r(c, d);
else {
if (a.source == parent && a.data.xdArbiterRegisterAck && j.test(d)) {
typeof a.data.xdArbiterRegisterAck === "string" && a.data.xdArbiterRegisterAck !== "" && p(a.data.xdArbiterRegisterAck);
g.isStarted() || g.start(function(a) {
if (a == null) {
b("Log").warn("Discarding null message from %s to %s", m, d);
return
}
var c = parent;
typeof a === "object" && typeof a.relation === "string" && (c = b("resolveWindow")(a.relation));
((c = c) != null ? c : parent).postMessage({
xdArbiterHandleMessage: !0,
message: a,
origin: m
}, d)
});
return
}
b("Log").warn("Received message of type %s from %s, expected a string. %s", typeof c, m, ES("JSON", "stringify", !1, c));
return
}
});
If we examine the code, we find that there is one Eventlistener for cross-origin messages inside the SDK. Once a message is received, it would check if the origin starts with https (or native) , and if it checks against the regex "/^https:\/\/.*facebook\.com$/"
As you may notice the regex was meant to target facebook.com and its subdomains however an escaped dot is missing before facebook.com ( correct one would be /^https:\/\/.*\.facebook\.com$/ ).
This allows a domain like testpocfacebook.com to pass the origin check done here.
The message sent by testpocfacebook.com would be {"xdArbiterRegisterAck":"canvas"} and the SDK would return an FB_RPC json formatted message to the parent window. The message would include "fallback_redirect_uri" which is the value of window.location.href.
Here comes the exploitation of this bug. Like explained above, the SDK includes code for all possible functionalities, a website could use the SDK to only use the sharing functionality however the code for the canvas functionality would still be included which means that the website would have an EventListener for cross origin messages which would now accept messages from the attacker owned website by exploiting the bug . As you may noticed in the code above, the source of the message is checked and it should be the parent window which means that for this to work, the targeted page must also allow itself to be iframed.
To conclude, If we can find a page in a website which includes the Facebook Javascript SDK and can be iframed, we can leak the window.location.href of that page.
How to abuse this ?