跳到主内容
@wquguru
精选88Hacker News Best(web_list)产品发布/更新

Parameter Labs披露ChatGPT沙箱逃逸致账户接管漏洞

We Took over ChatGPT Accounts

原文
发到 X
推荐理由

ChatGPT核心安全防线被突破,无需任何用户交互即可接管账户,对Agent生态和开发者信任度影响巨大,值得所有从业者关注其修复方案与后续影响。

Do the Math: How We Took Over ChatGPT Accounts By bypassing ChatGPT's sandbox and content security policy, our research team was able to take over anyone's account simply by sending them a link. Mukul GoyalOct 28, 202612 mins October 26, 2026Fixed P1Severity In October 2026, we found a way to turn an ordinary public ChatGPT share into attacker-controlled JavaScript running inside the signed-in session of whoever opened it. An attacker could create a malicious conversation, publish it using ChatGPT's normal sharing feature, and send the link to another user. When the victim opened the share, the payload executed as chatgpt.com with access to the victim's authenticated web session. We confirmed that this code could enumerate the victim's conversation list, retrieve content from a private conversation that had never been shared with the attacker, and send data from that conversation to an external server. The payload was not limited to modifying the page or displaying an alert—it could directly call ChatGPT's authenticated APIs as the victim, essentially allowing us to take over the victim's ChatGPT account. The victim did not need to install an App, join a Project, paste code, or open developer tools. Opening the shared link was the only required action. OpenAI fixed the issue on October 26 and classified the report as P1. The chain started with two features that looked harmless on their own: a graph and an HTML Preview. ``` flowchart TD A["Attacker publishes a ChatGPT conversation"] --> B["Victim opens the public share"] B --> C["HTML Preview crosses into the main chatgpt.com page"] C --> D["JavaScript runs in the victim's signed-in session"] D --> E["Any action on chatgpt.com as the victim:

算一算:我们如何通过绕过 ChatGPT 的沙箱和内容安全策略接管其账户。通过绕过 ChatGPT 的沙箱和内容安全策略,我们的研究团队能够仅通过向用户发送链接就接管任何人的账户。Mukul Goyal 2026年10月28日 12分钟 2026年10月26日 已修复 P1级严重性 在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 执行任何操作:

read private chats and files, change the account"] ``` ## Technical walkthrough ## It started with a graph and a Preview The question that started this research was simple: if Preview code was boxed into a different website, what could it still talk to? ChatGPT can turn an HTML code block into a small working webpage using its Preview feature. Since that page may contain arbitrary code, ChatGPT sensibly runs it inside a restricted frame on a different web address. The Preview can run JavaScript, but the browser treats it as a different website, so it cannot directly inspect the signed-in ChatGPT page around it. At first, this isolation appeared to work exactly as intended. Code inside the Preview could not read parent.document, which is the main ChatGPT page. That should have been the end of it. The conversation also contained a normal interactive graph. Behind the scenes, the graph loaded a math library called MathLive. Loading it did more than draw the graph: it also installed a listener in the main ChatGPT page for messages related to MathLive's on-screen keyboard. Browsers call the relevant API postMessage. It is a legitimate way for two frames to exchange information even when the browser prevents them from reading each other's pages. You can think of it as a mail slot: the restricted Preview cannot walk into the main page, but it can still drop a message through the slot. That is safe only if the receiver checks who sent the message. MathLive did not make that check for the setting we were interested in. Its listener accepted a message that changed the virtual keyboard layout, and that layout was allowed to contain HTML. The graph had installed the receiver; the Preview gave us an untrusted sender. Two unrelated features had accidentally created a path across the sandbox. ## The message that should not have worked The Preview sent two messages. The first replaced a virtual keyboard layout with our HTML. The second told MathLive to show the keyboard, causing it to render the new layout: ``` 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 }, "*"); ``` parameter MathLive recognized the public message type, but its top-level controller used an origin policy of none, which meant it accepted messages from any web address. It also did not require the update-setting message to come from a previously approved window. Anyone who could reach the listener and knew the message format could replace the layout. When the keyboard opened, MathLive joined the supplied layout into an HTML string and assigned it to innerHTML in the real ChatGPT page. A helper named createHTML initially looked like it might sanitize the value. It did not; it marked the string as trusted and passed our HTML through unchanged. This was exciting, but we still did not have JavaScript execution. Script tags inserted through innerHTML do not automatically run. The next step was an iframe using srcdoc, a browser feature that lets an embedded frame carry its HTML directly in an attribute: ``` ``` parameter MathLive inserted this frame into the main ChatGPT document without a sandbox attribute. That gave us two useful properties. First, srcdoc created a fresh document where an external script could execute. Second, because the main ChatGPT page created the frame, the browser gave it the https://chatgpt.com web identity. Code inside the new frame could therefore access the parent page. ## Bypassing CSP with YouTube JSONP ChatGPT's Content Security Policy, or CSP, stopped us again. CSP controls which scripts a page may load, and the policy correctly blocked inline JavaScript. It did, however, allow scripts from YouTube. YouTube's oEmbed endpoint supported a JSONP-style callback parameter. JSONP responses are JavaScript rather than ordinary JSON. The endpoint tried to reject an invalid callback, but it placed the callback into the response before returning its validation error: ``` https://www.youtube.com/oembed?callback=globalThis.__marker=31337;void ``` parameter The response began like this: ``` // API callback globalThis.__marker=31337;void({ "error": { "code": 400, "message": "Invalid JSONP callback name: ..." } }); ``` parameter Ending our callback with ;void made the object appended by YouTube harmless. Since YouTube was already allowed by ChatGPT's CSP, the browser loaded this response as a script and ran our callback inside the new frame. We now had arbitrary JavaScript running as chatgpt.com. The original Preview was still isolated on its separate web address; we had crossed the boundary by convincing a trusted component in the main page to create a new, unsandboxed document for us. ## Turning a lab bug into one public link So far, this was a useful sandbox escape, but the first working version was not a convincing real-world attack. It used a researcher-controlled ChatGPT App as the sender. A victim would have needed to connect or invoke that App first. We next tried delivering the payload through a native HTML attachment. ChatGPT opened the attachment inside the same sandboxed Preview, so it could send the malicious MathLive messages. However, when we exported the conversation as a public share, the attachment remained but the earlier graph did not. The graph was essential because it loaded MathLive and installed the vulnerable message listener in the main page. Without it, the attachment could still send the messages, but nothing was listening to or processing them. The breakthrough was putting both pieces directly into one ordinary public conversation: - A genuine graph initialized MathLive in the main page. - A later assistant-authored HTML Preview sent the two messages.

读取私人聊天和文件,更改账户"] ``` ## 技术详解 ## 始于图表和预览 开启这项研究的简单问题是:如果预览代码被隔离在不同的网站中,它还能与什么进行通信?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 识别了公共消息类型,但其顶层控制器使用了无来源策略(origin policy of none),这意味着它接受来自任何网页地址的消息。它也不要求 update-setting 消息必须来自之前批准的窗口。任何能够访问监听器并了解消息格式的人都可以替换布局。当键盘打开时,MathLive 将提供的布局合并为一个 HTML 字符串,并将其赋值给真实 ChatGPT 页面中的 innerHTML。一个名为 createHTML 的辅助函数起初看起来可能会对值进行清理。但它并没有这样做;它将字符串标记为受信任,并原样传递了我们的 HTML。这令人兴奋,但我们仍然没有实现 JavaScript 执行。通过 innerHTML 插入的 script 标签不会自动运行。下一步是使用带有 srcdoc 参数的 iframe,这是一个浏览器功能,允许嵌入帧直接在属性中携带其 HTML:``` ``` parameter 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 ``` parameter 响应以如下方式开始:``` // API callback globalThis.__marker=31337;void({ "error": { "code": 400, "message": "Invalid JSONP callback name: ..." } }); ``` parameter 以 ;void 结尾使 YouTube 附加的对象变得无害。由于 ChatGPT 的 CSP 已经允许 YouTube,浏览器将此响应作为脚本加载,并在新帧中运行我们的回调。我们现在拥有了以 chatgpt.com 身份运行的任意 JavaScript。原始的 Preview 仍然隔离在其单独的网络地址上;我们通过说服主页面中的受信任组件为我们创建一个全新的、无沙箱的文档,从而跨越了边界。## 将实验室漏洞转化为一个公开链接到目前为止,这是一个有用的沙箱逃逸,但第一个工作版本并不是一个令人信服的真实世界攻击。它使用研究人员控制的 ChatGPT App 作为发送者。受害者需要先连接或调用该 App。我们接下来尝试通过原生 HTML 附件传递有效载荷。ChatGPT 在相同的沙箱化 Preview 中打开附件,因此它可以发送恶意的 MathLive 消息。然而,当我们导出对话为公开分享时,附件保留了下来,但之前的图表却没有。该图表至关重要,因为它在主页中加载了 MathLive 并安装了易受攻击的消息监听器。如果没有它,附件仍然可以发送消息,但没有任何东西在监听或处理它们。突破点是将这两部分直接放入一个普通的公共对话中:- 一个真实的图表在主页中初始化了 MathLive。- 随后由助手撰写的 HTML 预览发送了这两条消息。

更进一步:量化金融体系

看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力

进入量化体系 →

关联讨论

同一事件的更多信源

相似阅读

关联信息,但可能不是同一事件