【API安全漏洞剖析】 FaceBook OAuth 漏洞 - 2020-03-01

信息来源: https://www.amolbaikar.com/facebook-oauth-framework-vulnerability/

漏洞描述

第三方网站如(Instagram)是可以通过 Facebook 账号进行登录的, 具体登录逻辑就是利用 Facebook 提供的 SDK,使用 OAuth 授权进行登录。

具体来说就是用户登录 ins, 然后 ins 会根据 FaceBook 的 SDK 规范, 跳转到 Facebook 对应的登录页面, Facebook 登录完成之后, 返回一个 access_token 给 ins, 完成登录。

黑客在这个 OAuth 授权流程能够窃取 access_token ,从而达到接管用户账号的目的。

漏洞成因

在 Facebook 提供给开发者接入的 SDK 中, 有一个名为 “/connect/ping” 的登录服务端, 它是提供给第三方应用获取用户访问令牌的第三方端点, 具体逻辑是开发者通过编码在后台创建跨域通信的代理 iframe , 再使用 window.postMessage() 方法接收令牌, 在接收令牌后, 该 API 会把链接跳转指向 Facebook 的 “XD_Arbiter” 下。 完整的 URL 为(以 ins 为例):

1
https://www.facebook.com/connect/ping?client_id=APP_ID&redirect_uri=https://staticxx.facebook.com/connect/xd_arbiter.php?version=42#origin=https://www.instagram.com

这里的 APP_ID 是第三方应用在 Facebook 注册时生成的应用 ID 值。

攻击者发现这里的 xd_arbiter.php?version=42 可以被篡改xd_arbiter/?version=42 , 然后在此基础下, 能够将目录附加到后面实现资源访问。

在这里由于通过上述目录获取到的 access_token 的相关值都是哈希片段, 很难去还原, 但是攻击者在 page_proxy 目录中发现了一个名为 7SWBAvHenEn.js 的 js 文件, 这个文件中包含如下内容:

1
2
var frameName = window.location.href.split("#")[1];
window.parent.postMessage(frameName,"*");

第一行代码会获取 URL 请求参数中 # 后面的内容, 将它赋值给 frameName , 然后调用 window.parent.postMessage 转发来自 frameName 发送的内容, 这里的 * 代表他可以接收任意源的请求。

然后攻击者构造 URL 为:

1
https://staticxx.facebook.com/connect/xd_arbiter/r/7SWBAvHenEn.js?version=42

就可以穿越访问到这个文件, 再通过 # 去指定源, 就可以拦截该源发送回来的 access_token .

这个 js 文件能够被攻击者利用, 原因如下:

  1. 攻击者能够通过资源附加访问到这个文件.
  2. postMessage() 方法会发送未加密的令牌.
  3. * 允许了攻击者能在自己的源 ip 下请求并获取响应.
  4. API 请求中没有验证 X-Frame-Options 标题,导致能够在 xd_arbiter 中嵌入跨域嵌套 postMessage() 回显的窗口.
  5. window.parent 下的方法不用与用户交互, 不需要关心window.open或任何onClick事件.

漏洞劫持

最后攻击者重写 Custom_SDK.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var app_id = '124024574287414',
app_domain = 'www.instagram.com';

var exploit_url = 'https://www.facebook.com/connect/ping?client_id=' + app_id + '&redirect_uri=https%3A%2F%2Fstaticxx.facebook.com%2Fconnect%2Fxd_arbiter%2Fr%2F7SWBAvHenEn.js%3Fversion%3D44%23origin%3Dhttps%253A%252F%252F' + app_domain;

var i = document.createElement('iframe');
i.setAttribute('id', 'i');
i.setAttribute('style', 'display:none;');
i.setAttribute('src', exploit_url);
document.body.appendChild(i);

window.addEventListener('OAuth', function(FB) {
alert(FB.data.name);
}, !1);

实现跨域攻击, 接管账户.

启示

作为防守方, 仅仅使用 URL 白名单来防御是不够的, 在面对跨域通信, 不同设备甚至浏览器的情况下, 即使使用了 X-Frame-Options 来防止跨域请求, 也有可能会因为兼容性问题产生漏洞.

在这里的启示就是, 应用设计时, 在实现功能的前提下, 要尽可能遵循简单原则, 参考标准协议实现流程, 线上环境不使用的文件, 要尽快清除.