工程趣聞 2026 年 9 月 25 日

2026-09-25 — ansi2html 跳脫漏洞讓帳號被接管

primary=https://blog.arusekk.pl/posts/srht-account-takeover/ primary=https://github.com/pycontribs/ansi2html/pull/263

ansi2html 的 OSC 8 跳脫漏洞,讓 SourceHut build log 變帳號接管入口

Arusekk's blog · 2026-08 disclosure write-up

SourceHut 的 build log 頁面會把 ansi2html.py 轉出的 HTML 直接嵌進頁面,而這個轉換器在處理終端機的 OSC 8 超連結逸出序列時,沒有把網址裡的雙引號跳脫,於是一段被 CI 印到 log 裡的字串就能在瀏覽者的瀏覽器裡插入任意 HTML 屬性、執行 JavaScript。安全研究者 Arusekk 在 2026 年 8 月回報這個問題,並示範如何把單一 XSS 串成完整的帳號接管,漏洞後來登記為 CVE-2026-92973(GHSA-98hw-5x94-6hwv)。

漏洞機制

OSC 8 是終端機用來標記可點擊超連結的逸出序列,格式大致是「網址」加上「顯示文字」。ansi2html.py 把它轉成 <a href="...">文字</a> 時,直接把網址字串塞進 href 屬性,沒有對雙引號做 HTML 屬性跳脫。作者示範的 payload 是這樣:

printf '\33]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\7Nothing to see here\33]8;;\7'

轉換後產生的 HTML 是:

<a href="https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`">Nothing to see here</a>

網址裡的雙引號提前關閉了 href 屬性,後面接的 autofocus、onfocus 就變成瀏覽器會執行的新屬性;javascript: 協定也是同樣路徑。攻擊者不需要 SourceHut 帳號:只要對外開放 CI 的公開 mailing list 送一份 patch,或讓 build 過程印出任何自己能控制的字串(例如 wget 抓檔案時的檔名),這段 payload 就會出現在 build log 裡,等其他人打開頁面時觸發。

build log 頁面本身就內嵌了當前使用者的 CSRF token,payload 執行後可以用 document.querySelector('[name=_csrf_token]').value 直接讀出來,再利用頁面上既有的「Resubmit build」表單,改掉 manifest 內容後呼叫 document.forms[0].submit() 送出。這一步能讓攻擊者取得受害者帳號在 build 上的權限,包括所有已設定的 deploy key——而 builds.sr.ht 本身也用同一套機制部署,其中就有 sr.ht 自己的 deploy key,等於一條 XSS 有機會一路打進 SourceHut 的基礎設施。

值得注意的是,這條路徑完全不需要受害者主動點擊任何東西——只要他打開一個被下了毒的 build log 頁面查看結果,payload 就會在他自己的瀏覽器、以他自己的 session 執行。對維護者與審核者來說風險特別高,因為他們正是最常打開別人送來的 patch 對應 build log 的人;一旦帳號被接管,攻擊者拿到的不只是這一個 build,而是該帳號能觸及的所有 deploy key 與後續操作權限。

受影響版本

套件受影響版本備註
ansi2html>=1.7.0, <1.9.4有問題的 OSC 8 處理邏輯於 2021-09-03 引入上游
builds.sr.ht>=0.40.0, <0.105.12022-02-08 起打包進 SourceHut 的 build 服務

修補與緩解

作者於 2026-08-01 回報並附上初步修補;SourceHut 維護者 Drew 在 2026-08-04 先在 builds.sr.ht 這一側加上對 ansi2html 輸出的額外消毒,堵住立即的攻擊面。上游修補則在 PR #263(延續 #260 的方向)於 2026-09-02 合併並隨 ansi2html 1.9.4 釋出,正確跳脫 OSC 8 網址與文字裡的特殊字元;Arch Linux 在 2026-09-05 跟進更新,CVE 編號則在 2026-09-17 正式配發。

作者也建議兩層防禦:一是頁面端移除 CSP 裡的 unsafe-inline,讓即使消毒漏掉個案也無法直接執行內嵌 script;二是把 ansi2html.py 這類逐段拼字串的轉換邏輯,重構成有明確狀態機的 transducer,而不是遇到一個逸出序列就臨時組一段 HTML。想自查歷史 log 是否被打過的人,可以用 grep $'\33]8;[^\7\33]*"' 掃 raw log,找有沒有 OSC 8 網址段落裡混了雙引號。

這個漏洞真正值得留意的地方不是 ansi2html 本身,而是它所代表的一類問題:任何把 CI/build log、terminal session 這類 ANSI 輸出轉成 HTML 顯示在網頁上的服務,都該檢查自己用的轉換器有沒有對 OSC 8 超連結、或其他會落進 HTML 屬性的欄位做跳脫,並確認同一頁面上有沒有 CSRF token、session 資訊等可被腳本讀取的敏感 DOM 內容——因為一旦兩者同時存在,攻擊者要的就不只是彈一個 alert,而是完整的帳號接管路徑。

原始來源:Account takeover on SourceHut via ansi2html XSS、ansi2html PR #263(修補 OSC 8 跳脫)


End of article
0
Would love your thoughts, please comment.x
()
x