跳到正文
Hacker News 熱門(buzzing.cc 中文翻譯)·· 1 天前精選AI 評分86

ChatGPT 公開分享連結漏洞可讓攻擊者接管用戶帳戶

我们接管了ChatGPT账户

AI 導讀

安全研究者披露,惡意 ChatGPT 公開分享連結可在開啟者已登入的工作階段執行 JavaScript,讀取私人對話內容並以用戶身份呼叫 ChatGPT API。OpenAI 於 2026 年 10 月 26 日確認兩個網頁前端均已修補相關跨框架訊息漏洞,並向研究者支付 $5,000 獎金。

推薦理由

文章還原 Preview、MathLive 訊息處理與公開分享如何串成漏洞,並交代私人對話外洩風險及 OpenAI 的修補措施。

正文 · 繁體中文

2026年10月,我們找到一種方法,可將一般的 ChatGPT 公開分享連結變成攻擊者控制的 JavaScript,並在任何開啟連結者已登入的工作階段內執行。

攻擊者可以建立惡意對話,使用 ChatGPT 的一般分享功能發佈,再把連結傳送給另一位用戶。受害者開啟分享連結時,負載便會以 chatgpt.com 的形式執行,並可存取受害者已驗證的網頁工作階段。

我們確認,這段程式碼可以列出受害者的對話清單、擷取一段從未與攻擊者分享的私人對話內容,並將其中的資料傳送至外部伺服器。負載不只可以修改頁面或顯示提示訊息,還能以受害者身分直接呼叫 ChatGPT 的已驗證 API,實際上讓我們能夠接管受害者的 ChatGPT 帳戶。

受害者毋須安裝 App、加入 Project、貼上程式碼或開啟開發人員工具。唯一需要做的,就是開啟分享連結。

OpenAI 於10月26日修復了這個問題,並將報告列為 P1。

這條攻擊鏈始於兩項單獨看來無害的功能:圖表和 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:<br/>read private chats and files, change the account"]

技術詳解


一切始於圖表和 Preview

這項研究始於一個簡單的問題:如果 Preview 程式碼被隔離在另一個網站內,它仍能與甚麼通訊?

ChatGPT 可透過 Preview 功能,將 HTML 程式碼區塊轉換成可運作的小型網頁。由於該頁面可能包含任意程式碼,ChatGPT 採取合理做法,將它放在另一個網址上的受限框架內執行。Preview 可以執行 JavaScript,但瀏覽器會將它視為另一個網站,因此它不能直接檢視周圍已登入的 ChatGPT 頁面。

起初,這種隔離看來完全符合預期。Preview 內的程式碼無法讀取 parent.document,也就是 ChatGPT 的主頁面。事情本應就此結束。

對話中還包含一個一般的互動式圖表。在背後,圖表載入了一個名為 MathLive 的數學函式庫。載入它不只會繪製圖表,還會在 ChatGPT 主頁面安裝一個監聽器,處理與 MathLive 螢幕鍵盤相關的訊息。

瀏覽器將相關 API 稱為 postMessage。這是一種合法方式,讓兩個框架即使在瀏覽器阻止它們讀取對方頁面的情況下,仍可交換資訊。你可以把它想像成郵箱投遞口:受限的 Preview 無法走進主頁面,但仍可透過投遞口傳送訊息。

只有在接收方核實訊息來源時,這才安全。

在我們關注的設定下,MathLive 沒有進行這項核實。它的監聽器接受一則可更改虛擬鍵盤版面的訊息,而版面內容可以包含 HTML。圖表安裝了接收器;Preview 則提供了不受信任的發送端。兩項互不相關的功能意外地在沙盒中打通了一條路徑。

本不應奏效的訊息

Preview 傳送了兩則訊息。第一則以我們的 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="..."></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。原本的 Preview 仍隔離在另一個網絡地址;我們是透過説服主頁面中的可信元件,為我們建立一個新的、不受沙盒限制的文件,從而跨越這道界線。

將實驗室漏洞變成一條公開連結

到目前為止,這是一次有效的沙盒逃逸,但首個可行版本還算不上令人信服的真實攻擊。它使用由研究人員控制的 ChatGPT App 作為傳送方。受害者必須先連結或啟用該 App。

接下來,我們嘗試透過原生 HTML 附件傳送攻擊酬載。ChatGPT 在同一個受沙盒限制的 Preview 中開啟附件,因此附件可以傳送惡意的 MathLive 訊息。

然而,將對話匯出並公開分享時,附件仍然保留,但先前的圖表卻沒有保留。圖表至關重要,因為它會在主頁面載入 MathLive,並安裝有漏洞的訊息監聽器。沒有圖表,附件仍可傳送訊息,但沒有任何元件會監聽或處理這些訊息。

突破在於將兩個部分都直接放進同一段一般的公開對話:

  1. 一個真正的圖表在主頁面初始化了 MathLive。
  2. 之後由助理撰寫的 HTML Preview 傳送了兩則訊息。

由此產生的 /share/<id> 連結保留了完整的攻擊鏈,讓其他用戶也能使用。攻擊者可以像傳送任何已分享的 ChatGPT 對話一樣,把連結透過電郵、聊天、支援請求或社交平台貼文傳送出去。受害者無須安裝任何軟件、加入攻擊者的工作空間,亦無須透過攻擊者控制的流程進行驗證。他們只需前往攻擊者分享的 ChatGPT 網址。

我們觀察到兩種互動版本。在最穩定的重現方式中,受害者開啟分享內容,並只按一下 ChatGPT 一般的 預覽 按鈕。其後,我們將 HTML 區塊標記為可預覽,讓 ChatGPT 自動開啟該區塊。在這個版本中,受害者只需按一下即可開啟連結。我們在另一個帳戶中重現了這種自動執行方式三次,包括強制重新載入後;警示約在 4.5 秒後出現。

圖表和預覽各自載入,因此載荷偶爾會在 MathLive 就緒前發送訊息。最終載荷透過在有限時間內重試發送相同的兩則訊息,處理了這個競態情況。主頁中的防護條件確保私人對話的證明及外發請求只會執行一次。

The public share modified the real ChatGPT page
公開分享內容修改了真正的 ChatGPT 頁面

此時,公開分享內容可以顯示一個由瀏覽器識別為來自 chatgpt.com 的警示,並在主頁加入橫幅。這證明瞭我們的程式碼在哪裏執行,但尚未説明這個漏洞為何重要。

只有警示並不足夠

此時,顯示警示是這項漏洞利用所能做到最無關緊要的事。JavaScript 以 chatgpt.com 的身分執行,這表示它能使用與一般網頁應用程式相同的已驗證工作階段,與 ChatGPT 互動。

載荷讀取頁面本身已可存取的工作階段資料,並利用這些資料呼叫 ChatGPT 的對話 API。首先,它列舉受害者最近的對話,然後可以要求取得指定私人對話的訊息資料:

GET /backend-api/conversations?...&limit=20&order=updated&offset=0
GET /backend-api/conversations/<conversation-id>?num_turns=2

這是直接以已登入狀態存取 API,並非擷取畫面上剛好可見的文字。目標對話與惡意分享內容無關,而且從未向攻擊者分享。

注入的 JavaScript 取得回應後,便可輕易將資料傳送至外部。載荷向我們控制的伺服器發出一般的圖片請求。

我們刻意只將回應中的一小部分傳送至外部。注入的程式碼已取得經驗證的對話回應,並能讀取其內容,因此漏洞的影響並不侷限於該項資料。

Controlled proof that private conversation data could leave the victim's session
受控測試證明,私人對話資料可離開受害者的工作階段

結果是一種切實可行的跨用戶資料竊取手段。惡意的公開對話可在另一位用戶已登入的 ChatGPT 工作階段中執行,列舉私人對話、取得私人訊息內容,並將選定資料傳送至攻擊者控制的伺服器。

由於程式碼使用網頁應用程式的已驗證存取權限執行,其可觸及的範圍本身並不侷限於我們用作證明的對話端點。ChatGPT 已驗證前端 API 所提供的其他資料及操作,都處於同一安全邊界內。基本上,我們可以代表受害者的 ChatGPT 帳戶執行任何操作。

出了甚麼問題,以及 OpenAI 作出了哪些修正

預覽沙箱本身運作正常。預覽內的程式碼無法直接讀取 ChatGPT 頁面。問題在於,主頁中的另一個元件會接受來自它的指令。

三項設計決定令這個錯誤演變成完整的漏洞利用:

  1. MathLive 接受會改變狀態的訊息,卻沒有確認訊息由哪個框架發送。
  2. 訊息格式容許將原始 HTML 作為鍵盤配置資料提供。
  3. 該 HTML 可建立一個不受沙箱限制的文件,並繼承 chatgpt.com 的來源身分。

YouTube 的回應是繞過 CSP 的最後一個方法,並非根本原因。只封鎖該端點,只會移除其中一條執行 JavaScript 的途徑,仍會讓不受信任的 Preview 能夠將 HTML 插入 ChatGPT。

OpenAI 重現了 JavaScript 執行及私人對話資料外洩的證明。修正已部署至 ChatGPT 網頁前端的兩個版本。部署期間,CSP 變更封鎖了原有攻擊鏈的部分環節,但其中一個版本仍需要更新。10 月 26 日,OpenAI 確認兩個版本現在都會拒絕報告中使用的跨框架 MathLive 訊息,並將問題標記為已解決。

防禦方面的要點相當簡單:

  • 訊息接收端應同時驗證發送者的網址,以及獲準傳送訊息的確切視窗。
  • 跨視窗設定不應包含原始 HTML。
  • 當文件內容可能受不受信任的框架影響時,動態建立的文件應維持在沙盒中。
  • CSP 應是備援防護措施,而非攻擊者控制的 HTML 到達主網站後的唯一屏障。

這個漏洞值得注意,因為每項個別功能看起來都合情合理。Preview 獨立隔離。圖表可以載入數學鍵盤。框架可以互相傳送訊息。CSP 允許主要媒體供應商的內容。公開對話可以分享。只有在同一頁面結合所有這些功能時,漏洞才會出現。

時間線

日期事件
2026 年 10 月 13 日確認 MathLive 訊息轉為 HTML 的路徑在 ChatGPT 正式環境程式碼中成立。
10 月 14 日透過一般介面重現整條攻擊鏈,並將固定的警示升級為任意 JavaScript 執行。
10 月 16 日確認只需點擊一次 Preview,即可在不同帳戶和工作區之間透過公開分享傳遞。
10 月 17 日確認透過分享內容導覽時會自動載入 Preview,並在強制重新載入後重現。
10 月 18 日向 OpenAI 漏洞賞金計劃提交報告。
10 月 19 日補上受控的私人對話資料外洩證明。
10 月 21 日OpenAI 重現問題;嚴重程度由初步 P2 分流評級提升至 P1。
10 月 22 日OpenAI 表示其中一個網頁前端已修正,另一個仍在更新。
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;這項政策會封鎖內嵌程式碼,但允許在 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 將它編碼至回呼中;指令碼網址在 <script src> 內經過 HTML 跳脱,而整個元素再經過一次跳脱,放入 iframe 的 srcdoc 屬性。最終回呼長 5,533 個字元;YouTube 網址長 7,309 個字元;指令碼元素長 7,337 個字元;最終 iframe 標記長 7,390 個字元。

經身份驗證的讀取。同源框架從父文件解析 #client-bootstrap,擷取工作階段存取權杖和帳戶 ID,並要求權杖中的帳戶 ID 與工作階段中的帳戶 ID 一致(兩者均存在時)。它使用了以下記憶體中的標頭:

Authorization: Bearer <victim token>
ChatGPT-Account-ID: <victim account>
Accept: application/json

主要請求如下:

GET /backend-api/conversations?exclude_conversation_origin=tpp&expand=false&is_archived=false&is_starred=false&limit=20&order=updated&offset=0
GET /backend-api/conversations/<id>?num_turns=2&include_has_versions=true

只有詳細資料請求傳回 403、404 或 405 時,程式碼才會嘗試使用舊版 /backend-api/conversation/<id>?include_full_conversation=true 路由。驗證只會傳送回應中的一個小型選定值。在發出外部圖片請求前,它清除了對存取權杖、帳戶、啟動資料、工作階段和請求標頭的參照。

重點

  • 隔離的強度取決於所有跨越隔離邊界的橋接點。即使框架已設置沙盒,只要具特權的監聽器信任了錯誤發送者的訊息,該框架仍可危害其父框架。
  • postMessage 接收端在接受會改變狀態的指令前,必須同時驗證 event.origin 和 event.source。
  • HTML 注入可透過新建、未設沙盒的文件及獲準使用的第三方指令碼端點,演變成 JavaScript 執行。CSP 是重要的縱深防禦措施,但無法令不安全的呈現流程變得安全。
  • 必須在受害者已驗證的情境下評估同源 JavaScript 的影響。一旦程式碼以 chatgpt.com 身份執行,便可存取一般前端可用的相同私人資料及帳戶操作,而不只是顯示警示。
  • 跨功能測試十分重要。這條攻擊鏈存在於公開分享、Preview、MathLive、框架建立及 CSP 之間的接合處,而非單獨檢視的任何一項功能之中。

更廣泛的教訓是,安全邊界必須在整個產品中都能有效維持,而不只是個別元件內。任何公開或可分享的介面都應視為由攻擊者控制的輸入;每則跨框架訊息都應經過驗證;不可信的標記則應在整個呈現流程中持續置於沙盒內。

來源:Hacker News 熱門(buzzing.cc 中文翻譯) · parameter.ai