我们接管了 ChatGPT 账户

Hacker News Top 新闻

摘要

Parameter Labs 的研究人员披露了一个跨框架消息传递漏洞:该漏洞将 ChatGPT 的 HTML Preview 沙箱与 MathLive 未经验证的 postMessage 监听器串联起来,使攻击者可以通过一个共享对话在受害者已登录的会话中执行 JavaScript,从而接管其账户。OpenAI 已于 10 月 26 日修复了这一 P1 级问题,用户无需进行任何额外操作,只要点击链接即可触发攻击。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/31 12:18

# 算一笔账:我们是如何接管 ChatGPT 账户的 来源:https://www.parameter.ai/labs/we-found-a-gap-at-openai 2026年10月,我们发现了一种方法,可以将普通的公开 ChatGPT 分享转化为在已登录会话中运行的攻击者可控 JavaScript——只要有人打开该分享即可生效。攻击者可以创建一个恶意对话,通过 ChatGPT 的正常分享功能将其发布,然后将链接发送给另一个用户。当受害者打开分享时,载荷会以 `chatgpt.com` 的身份执行,并访问受害者的已认证 Web 会话。我们确认,该代码可以枚举受害者的对话列表、获取一个从未与攻击者共享过的私有对话内容,并将该对话中的数据发送到外部服务器。该载荷不仅限于修改页面或显示弹窗——它可以代表受害者直接调用 ChatGPT 的已认证 API,本质上使我们能够接管受害者的 ChatGPT 账户。受害者无需安装任何应用、加入任何项目、粘贴代码或打开开发者工具。打开分享链接是唯一需要的操作。 OpenAI 于 10 月 26 日修复了该问题,并将此报告定级为 P1。整个攻击链始于两个单独看起来无害的功能:一个图表和一个 HTML 预览。 ``` flowchart TD A["攻击者发布一个 ChatGPT 对话"] --> B["受害者打开公开分享"] B --> C["HTML 预览跨越到主 chatgpt.com 页面"] C --> D["JavaScript 在受害者的已登录会话中运行"] D --> E["以受害者身份在 chatgpt.com 上执行任何操作:读取私有对话和文件、修改账户"] ``` ### 技术详解 --- ### 一切始于一个图表和一个预览 这个研究的起点很简单:如果预览代码被隔离在一个不同的网站上,它还能与什么通信?ChatGPT 可以通过其"预览"功能将 HTML 代码块转化为一个小型可运行的网页。由于该页面可能包含任意代码,ChatGPT 合理地将其运行在一个不同网络地址的受限框架中。预览可以运行 JavaScript,但浏览器将其视为不同的网站,因此它无法直接检查周围的已登录 ChatGPT 页面。 起初,这种隔离看起来完全按预期工作。预览内部的代码无法读取 `parent.document`,即主 ChatGPT 页面。这本应是终点。 该对话还包含一个普通的交互式图表。在幕后,该图表加载了一个名为 MathLive 的数学库。加载它不仅仅是绘制图表:它还在主 ChatGPT 页面上安装了一个监听器,用于处理与 MathLive 屏幕键盘相关的消息。浏览器将相关 API 称为 `postMessage`。这是一种合法的方式,让两个框架即使在浏览器阻止它们读取彼此页面的情况下也能交换信息。你可以将其想象成一个投信口:受限的预览无法走进主页面,但它仍然可以通过投信口递送消息。这只有在接收者检查消息发送者身份时才是安全的。MathLive 对我们感兴趣的那个设置并没有做这个检查。它的监听器接受了一条更改虚拟键盘布局的消息,而该布局允许包含 HTML。图表安装了接收器;预览给我们提供了一个不可信的发送者。两个不相关的功能意外地在沙箱之间创建了一条通道。 ### 本不应生效的消息 预览发送了两条消息。第一条用我们的 HTML 替换了虚拟键盘布局。第二条告诉 MathLive 显示键盘,使其渲染新布局: ``` top.postMessage({ type: "mathlive#virtual-keyboard-message", action: "update-setting", layouts: [{ label: "Compatibility", layers: [{ id: "compatibility", markup }] }] }, "*"); top.postMessage({ type: "mathlive#virtual-keyboard-message", action: "show", animate: false }, "*"); ``` MathLive 识别了公开的消息类型,但其顶层控制器使用了 `none` 的源策略,这意味着它接受来自任何网络地址的消息。它也不要求 `update-setting` 消息来自先前已批准的窗口。任何能够访问监听器并知道消息格式的人都可以替换布局。当键盘打开时,MathLive 将提供的布局拼接为一个 HTML 字符串,并将其赋值给真实 ChatGPT 页面中的 `innerHTML`。一个名为 `createHTML` 的辅助函数起初看起来可能对值进行了净化。实际上并没有;它将字符串标记为可信的,并原样传递了我们的 HTML。 这很令人兴奋,但我们仍然没有实现 JavaScript 执行。通过 `innerHTML` 插入的 script 标签不会自动运行。下一步是使用带有 `srcdoc` 的 `iframe`,这是一种浏览器功能,允许嵌入的框架直接在属性中携带其 HTML: ``` <iframe srcdoc="<script src='...'></script>"></iframe> ``` MathLive 将此框架插入主 ChatGPT 文档中,且没有 `sandbox` 属性。这给了我们两个有用的特性。首先,`srcdoc` 创建了一个全新的文档,外部脚本可以在其中执行。其次,由于主 ChatGPT 页面创建了该框架,浏览器赋予它 `https://chatgpt.com` 的网络身份。因此,新框架内的代码可以访问父页面。 #### 使用 YouTube JSONP 绕过 CSP ChatGPT 的内容安全策略(CSP)再次阻止了我们。CSP 控制页面可以加载哪些脚本,该策略正确地阻止了内联 JavaScript。然而,它允许来自 YouTube 的脚本。YouTube 的 oEmbed 端点支持 JSONP 风格的回调参数。JSONP 响应是 JavaScript 而非普通 JSON。该端点试图拒绝无效的回调,但在返回验证错误之前将回调放进了响应中: ``` https://www.youtube.com/oembed?callback=globalThis.__marker=31337;void ``` 响应开头如下: ``` // API callback globalThis.__marker=31337;void({ "error": { "code": 400, "message": "Invalid JSONP callback name: ..." } }); ``` 在回调结尾加上 `;void` 使 YouTube 追加的对象变得无害。由于 ChatGPT 的 CSP 已经允许 YouTube,浏览器将此响应作为脚本加载,并在新框架中执行了我们的回调。我们现在拥有了以 chatgpt.com 身份运行的任意 JavaScript。原始的预览仍然隔离在其独立的网络地址上;我们通过说服主页面中一个可信组件为我们创建一个新的、无沙箱的文档,从而跨越了边界。 ### 将实验室漏洞转化为一个公开链接 到目前为止,这是一个有用的沙箱逃逸,但第一个可用版本并不是一个令人信服的现实攻击。它使用了一个研究者控制的 ChatGPT App 作为发送者。受害者需要先连接或调用该 App。我们随后尝试通过原生 HTML 附件传递载荷。ChatGPT 在同一个沙箱化的预览中打开附件,因此它可以发送恶意的 MathLive 消息。然而,当我们将对话导出为公开分享时,附件保留了,但之前的图表没有了。图表是关键,因为它加载了 MathLive 并在主页面中安装了有漏洞的消息监听器。没有它,附件仍然可以发送消息,但没有东西在监听或处理这些消息。 突破在于将两部分直接放入一个普通的公开对话中: 1. 一个真正的图表在主页面中初始化 MathLive。 2. 之后由助手编写的 HTML 预览发送那两条消息。 生成的 `/share/` 链接为另一个用户保留了完整的攻击链。攻击者可以用与发送任何共享 ChatGPT 对话相同的方式传递它——通过电子邮件、聊天、支持工单或社交媒体帖子。受害者无需安装任何东西、加入攻击者的空间或通过攻击者控制的流程进行身份验证。他们只需访问攻击者的共享 ChatGPT URL。 我们观察到了两种交互版本。在最稳定的复现中,受害者打开分享并点击 ChatGPT 正常的**预览**按钮一次。后来我们将 HTML 块标记为可预览,使 ChatGPT 自动打开它。在该版本中,打开链接是受害者唯一的一次点击。我们在一个独立账户中三次复现了自动路径,包括在强制刷新后,弹窗大约在 4.5 秒后出现。图表和预览独立加载,因此载荷偶尔会在 MathLive 就绪之前发送消息。最终的载体通过在有限时间内重试相同的两条消息来处理这个竞态条件。主页面中的一个守卫确保私有对话证明和外发请求只运行一次。 公共分享修改了真实的 ChatGPT 页面 到此,公开分享可以显示一个被浏览器识别为来自 `chatgpt.com` 的弹窗,并且可以在主页面上添加横幅。这证明了我们的代码在何处运行。但它还没有展示为什么这个漏洞重要。 ### 一个弹窗远远不够 此时,显示弹窗是这个漏洞最不有趣的事情。JavaScript 正在以 `chatgpt.com` 的身份运行,这意味着它可以使用与正常 Web 应用程序相同的已认证上下文与 ChatGPT 交互。载荷读取了页面中已有的会话信息,并使用它来调用 ChatGPT 的对话 API。 首先,它枚举了受害者的最近对话。然后它请求一个选定私有对话的消息数据: ``` GET /backend-api/conversations?...&limit=20&order=updated&offset=0 GET /backend-api/conversations/?num_turns=2 ``` 这是直接的已认证 API 访问,而不是抓取恰好在屏幕上可见的文本。目标对话与恶意分享无关,且从未与攻击者共享过。 一旦注入的 JavaScript 获得了响应,将数据发送到站外就是简单的事情。载荷创建了一个指向我们控制的服务器的普通图片请求。我们刻意只将响应的一小部分发送到站外。注入的代码已经接收了已认证的对话响应并可以读取其内容,因此该漏洞不限于该值。 可控的证明:私有对话数据可以离开受害者的会话 其结果是一个实用的跨用户数据窃取原语。一个恶意的公开对话可以在另一个用户的已登录 ChatGPT 会话中执行,枚举私有对话,检索私有消息内容,并将选定的数据发送到攻击者控制的服务器。由于代码以 Web 应用的已认证访问权限运行,其触及范围本质上不限于我们证明中使用的对话端点。通过 ChatGPT 已认证前端 API 暴露的其他数据和操作位于相同的安全边界内。基本上,我们可以代表受害者的 ChatGPT 账户执行任何操作。 ### 哪里出了问题以及 OpenAI 修复了什么 预览沙箱本身是正常的。预览内部的代码无法直接读取 ChatGPT 页面。问题在于主页面中的另一个组件愿意接受它的指令。三个设计决策将这个错误变成了一个完整的漏洞利用: 1. MathLive 在未验证哪个框架发送了消息的情况下接受了一条改变状态的消息。 2. 消息格式允许提供原始 HTML 作为键盘布局数据。 3. 该 HTML 可以创建一个无沙箱的文档,继承 `chatgpt.com` 身份。 YouTube 响应是绕过 CSP 的最终手段,而非根本原因。仅阻止该端点将消除一条通往 JavaScript 的路径,但仍会留下不可信的预览能够向 ChatGPT 插入 HTML 的问题。 OpenAI 复现了 JavaScript 执行和私有对话证明。修复分两个版本的 ChatGPT Web 前端逐步推出。在推出期间,CSP 变更阻止了原始攻击链的一部分,而一个版本仍需要更新。10 月 26 日,OpenAI 确认两个版本现在都拒绝了报告中使用的跨框架 MathLive 消息,并标记该问题已解决。 防御方面的教训相当简单: - 消息接收者应同时验证发送者的网络地址和被授权的具体窗口。 - 跨窗口设置不应包含原始 HTML。 - 当动态创建的文档内容可以被不可信框架影响时,应保持沙箱化。 - CSP 应作为备份控制措施,而非攻击者可控 HTML 到达主站点后的唯一屏障。 这个漏洞有趣之处在于每一个单独的功能看起来都是合理的。预览是隔离的。图表可以加载数学键盘。框架可以交换消息。CSP 允许一个主要的媒体提供商。公开对话可以被分享。漏洞仅在所有这些功能组合在一个页面上时才出现。 ### 时间线 | 日期 | 事件 | | --- | --- | | 2026年10月13日 | 在 ChatGPT 生产代码上确认了 MathLive 消息到 HTML 的路径。 | | 10月14日 | 通过正常 UI 复现了攻击链,并将固定的弹窗升级为任意 JavaScript。 | | 10月16日 | 确认了在独立账户和工作空间之间通过一次预览点击进行公开分享传递。 | | 10月17日 | 确认了从分享导航自动触发预览,并在强制刷新后复现。 | | 10月18日 | 向 OpenAI 的漏洞赏金计划提交了报告。 | | 10月19日 | 补充了可控的私有对话泄露证明。 | | 10月21日 | OpenAI 复现了该问题;严重性从初始的 P2 分级提升至 P1。 | | 10月22日 | OpenAI 报告一个 Web 前端已修复,第二个仍在更新中。 | | 10月23日 | CSP 变更阻止了攻击链的一部分,而一个前端更新仍未完成。 | | 10月26日 | OpenAI 确认两个前端都拒绝了报告中的消息,解决了该问题,并授予总计 $5,000 的赏金。 | ### 技术备注 上面的主线包含完整的漏洞利用链。以下细节供希望更仔细地跟踪生产代码和载荷转换的读者参考。 相关的反混淆控制流等价于: ``` window.addEventListener("message", (event) => { if (event.data?.type !== "mathlive#virtual-keyboard-message") return; if (!validateOrigin(event.origin, this.originValidator)) return; this.handleMessage(event.data, event.source); }); if (message.action === "update-setting" && message.layouts) { this.layouts = message.layouts; } ``` 布局规范化过程保留了 `layouts[].layers[].markup`。显示键盘最终执行了等价于以下的操作: ``` keyboardContainer.innerHTML = MathfieldElement.createHTML(keyboardHTML); ``` `createHTML` 充当的是恒等函数或可信类型(Trusted Types)包装器,而非净化器。Trusted Types 可以限制谁可以向 HTML 接收器赋值,但一个恒等策略并不能使攻击者可控的字符串变得安全。 **解析器与 CSP 转换。** `innerHTML` 操作可以创建 iframe,但以该方式直接插入的脚本将保持无效状态。无沙箱的 `srcdoc` 导致了一次新的 HTML 文档解析,并继承了嵌入 ChatGPT 的源。该文档还继承了测试期间捕获的有效 CSP,该 CSP 阻止了内联代码但在 `script-src-elem` 中允许了 `www.youtube.com`。oEmbed 响应返回了 HTTP 200,`Content-Type: text/javascript; charset=UTF-8` 和 `X-Content-Type-Options: nosniff`;未观察到 Cross-Origin-Resource-Policy 头。Google 在回调中将尖括号进行了转写,因此载荷使用了传统的 `function` 语法而非箭头函数。以 `;void` 结束程序消耗了端点追加的错误对象。 对于私有对话证明,函数通过 `toString()` 转换为源代码,双引号转换为单引号,空格被折叠。然后通过 `URLSearchParams`

相似文章

我们如何接管 ChatGPT 账户

Simon Willison's Blog

Parameter Labs 披露了一个针对 ChatGPT HTML 预览产物的复杂攻击链,可实现沙箱逃逸、JavaScript 执行,并通过精心构造的分享链接窃取用户的私密数据。OpenAI 已修复该漏洞,并支付了 5,000 美元的漏洞赏金,但外界批评对于如此严重的漏洞而言金额过低。

ChatGPT Mac 应用存在一处漏洞,黑客或可借此窃取敏感数据

Wired

Objective-See 基金会的研究人员在 macOS 版 ChatGPT 应用中发现了一个「极其简单」的漏洞,攻击者可借此完全控制该应用、访问聊天记录,并以合法 OpenAI 软件的身份执行命令。OpenAI 已于 9 月 25 日在更新日志中公开承认了这一漏洞及其修复方案。

3月20日 ChatGPT 故障:发生了什么

OpenAI Blog

OpenAI 在 3 月 20 日因为一个开源库中的漏洞导致 ChatGPT 离线,该漏洞向部分用户暴露了聊天历史标题和支付信息。此次事件影响了 1.2% 的 ChatGPT Plus 订阅用户,在九小时的时间窗口内暴露了部分信用卡详情和个人信息,但完整的信用卡号码从未泄露。