Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-34212 — Docmostは、アタッチメントノード内のjavascript: URLを受け入れ、それをストレージとレンダリングを通じて保持し、Docmostオリジン内でクリック可能なアンカーに変換しました。 | Kitploit
ツール/GitHubGitHub/0xmrma/cve-2026-34212
脆弱性分析エクスプロイトウェブセキュリティペネトレーションテスト論文と研究学習と教育
GitHub0xmrma/cve-2026-34212

CVE-2026-34212

Docmostは、アタッチメントノード内のjavascript: URLを受け入れ、それをストレージとレンダリングを通じて保持し、Docmostオリジン内でクリック可能なアンカーに変換しました。

リポジトリを見る
1ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2026-34212

Docmost は、アタッチメントノード内の javascript: URL を受け入れ、それをストレージとレンダリングを通じて保持し、Docmost オリジン内のクリック可能なアンカーに変換していました。

概要

私は Docmost(オープンソースの共同作業ドキュメントプラットフォーム)において、高深刻度の保存型XSS の問題を特定し、責任を持って開示し、再現しました。

Docmost の公式サイトでは、エンタープライズ対応のオンプレミス Wiki として 300 万以上のダウンロード があり、ビリニュス市、Bechtle、オーストラリア政府、赤十字社、ETS Quebec などの組織のチームから信頼されていると謳っています。

このバグは、リッチテキストシステムで見落とされがちな場所にありました。

通常のリンク拡張ではなく、ファイルアタッチメントに使用される別のカスタムノードタイプ内です。

私は、非常に具体的な疑問を念頭に置いてエディタパイプラインをレビューしていました。

通常のリンクが javascript: URL をブロックする場合、アタッチメントノードはアンカーシンクに到達する前に同じルールを適用するのか?

脆弱なバージョンでは、適用していませんでした。

Docmost はページ JSON 内の悪意のあるアタッチメントノードを受け入れ、その url 属性を変更せずに保存し、後でその値をクリック可能な <a href="javascript:..."> 要素にレンダリングしていました。

この問題が CVE-2026-34212 になりました。

Docmost: docmost/docmost
アドバイザリ: GHSA-cf68-cff9-hq4w
CVE: CVE-2026-34212
修正バージョン: v0.71.0

photo0

攻撃チェーン

攻撃者が制御するアタッチメントノードURL → ページJSONとして受け入れられ、変更されずに保存 → HTML/ReactレンダリングがそのURLをアンカーhrefに変換 → 被害者がアタッチメントアクションをクリック → 攻撃者が制御するJavaScriptがDocmostオリジンで実行


Docmostのこの部分の機能

Docmost はページコンテンツを ProseMirror/Tiptap 互換の JSON 形式で保存します。

そのコンテンツモデルには、以下のようなカスタムブロックノードが含まれています:

  • 画像
  • 図
  • 埋め込み
  • アタッチメント

アタッチメントノードは次のようなフィールドを保存します:

  • url
  • name
  • mime
  • size
  • attachmentId

サーバーはページコンテンツをいくつかの形式で受け入れます:

  • json
  • markdown
  • html

そして、保存する前にそれらを ProseMirror JSON に正規化します。

つまり、URL を持つ可能性のあるノードタイプはすべて、直接の信頼境界の一部です。

それらのノードタイプのいずれかが最終的に <a href> にレンダリングされる場合、URL スキームの処理はオプションではありません。 それはセキュリティモデルの一部です。


この表面を調査する価値があった理由

カスタムエディタ拡張は、セキュリティのずれが頻繁に発生する原因です。

ベースシステムはすでに危険なURLを正しく処理する方法を知っているかもしれませんが、各カスタムノードは独自のシンクで同じルールを再適用する必要があります。

これにより、予測可能なレビュー戦略が生まれます:

  • URLのようなフィールドを保存するノードタイプをすべて見つける
  • そのフィールドが受け入れられる場所を追跡する
  • そのフィールドがレンダリングされる場所を追跡する
  • そのサニタイズ動作をプラットフォームの通常のリンク処理と比較する

これこそが、このバグを露呈させたものです。

Docmost の通常のリンク拡張はすでに javascript: を危険として扱っていました。

そのアタッチメントノードはそうではありませんでした。

この非対称性を見れば、セキュリティ上の疑問は明らかです:

url が javascript: であるアタッチメントノードを永続化し、それをライブアンカーにレンダリングさせることができるか?

答えは「はい」でした。


根本原因

根本原因は コンテンツノードタイプ間での URL サニタイズの不整合 でした。

サーバー側のコンテンツパスは、全体的なコンテンツが ProseMirror スキーマに一致する限り、任意のアタッチメント URL を受け入れました。

脆弱なバージョンでは:

  • CreatePageDto は content?: string | object を受け入れました
  • PageService.parseProsemirrorContent() は markdown、html、または json を正規化しました
  • 次にサーバーは jsonToNode(prosemirrorJson) を呼び出しました
  • スキーマ検証が成功すると、コンテンツが保存されました

この検証ステップは構造的な正当性をチェックしましたが、URL の安全性はチェックしませんでした。

脆弱なサーバーロジックの重要な部分は事実上次のようでした:

root@kitploit:~
prosemirrorJson = content;
jsonToNode(prosemirrorJson);
return prosemirrorJson;

ここではアタッチメントURLスキームの正規化は行われませんでした。

その後、アタッチメント拡張が攻撃者が制御する値を直接レンダリングしました。

脆弱なアタッチメントノードは次のように行いました:

root@kitploit:~
url: {
  default: "",
  parseHTML: (element) => element.getAttribute("data-attachment-url"),
  renderHTML: (attributes) => ({
    "data-attachment-url": attributes.url,
  }),
},

そして:

root@kitploit:~
[
  "a",
  {
    href: HTMLAttributes["data-attachment-url"],
    class: "attachment",
    target: "blank",
  },
  `${HTMLAttributes["data-attachment-name"]}`,
]

クライアント側では、React ノードビューがそれを再び次のようにラップしました:

root@kitploit:~
<a href={getFileUrl(url)} target="_blank">

しかし、getFileUrl() は以下の場合のみ特別に処理しました:

  • 絶対 http URL
  • /api/...
  • /files/...

それ以外はすべて変更されずに返されました。

したがって、次のようなペイロード:

root@kitploit:~
javascript:alert(document.domain)

は、以下のすべてを生き延びました:

  • JSON ストレージ
  • サーバー側スキーマ検証
  • HTML レンダリング
  • クライアント側 URL 処理

これだけで、保存型 XSS には十分でした。

根本原因が特に明確になるのは、比較ポイントです。

Docmost の通常のリンク拡張は javascript: を明示的にブロックしていました:

  • parseHTML() で javascript: を拒否
  • renderHTML() で javascript: href を空白に設定

したがって、製品はこのスキームが危険であることをすでに認識していました。

アタッチメントノードは単に同じポリシーを適用できなかっただけです。

これが「エディタの汎用 XSS」ではなかった理由です。

これはノード固有の信頼境界のギャップでした。


これが単なるサニタイズの欠如ではなく、セキュリティ問題である理由

このバグは、安全でない HTML の美学に関するものだけではありません。

ページを編集できる攻撃者が、他のユーザーがレンダリングされたアタッチメントを操作したときに後に Docmost オリジンで実行される悪意のあるペイロードを永続化できることを意味していました。

これは、オリジン内のスクリプトが以下のことを実行できるため重要です:

  • 被害者がアクセスできるデータを読み取る
  • 被害者として認証されたリクエストを送信する
  • 被害者が変更を許可されているコンテンツを変更する
  • セッションに公開されている DOM や API サーフェスを悪用する

クリックが必要であることは、この問題を軽微なものにしません。

クリックは通常の製品動作の一部です: UI は意図的にアタッチメントをアクション可能なリンク/アイコンとして提示します。

したがって、セキュリティ上の質問は「攻撃者は何の操作もなしに任意の JS を強制できるか?」ではありません。

本当の質問は:

アプリケーションは、攻撃者が制御するスクリプトを含むコンテンツを保存し、後で信頼された操作パスとして他のユーザーに提示するか?

脆弱なバージョンでは、そうしていました。

それが保存型 XSS です。


悪用が現実的であった理由

悪用パスは単純でした:

  • ページ編集権限を持つユーザーは誰でもペイロードを仕込める
  • 悪意のある URL は変更されずに保存を生き延びる
  • ページは通常通りレンダリングされる
  • 閲覧者はページへの標準的なアクセス権のみ必要
  • アタッチメントアクションを1回クリックするだけでトリガーに十分

これにより、高権限ユーザーも現実的な標的になりました。

ワークスペース所有者、管理者、または広く信頼されているエディタが攻撃者が制御するコンテンツを表示し、アタッチメントアクションをクリックすると、攻撃者のスクリプトはそのより権限の高いセッションコンテキストで実行されます。

これが重要な実際的なポイントです:

攻撃者の必要な権限は 低 だけでした。 被害者の権限レベルが、XSS セッションがどれだけの価値を持つかを決定しました。


概念実証

私は Docmost v0.70.3 に対してこの問題をライブで検証しました。

PoC は通常の HTTP リクエストとアプリケーション独自のページ API のみを使用しました。

フローは次のとおりです:

  1. ページを編集できるユーザーとしてログインします。
  2. ページを作成または選択します。
  3. format: "json" と、url が javascript: ペイロードであるアタッチメントノードを含む POST /api/pages/update を送信します。
  4. POST /api/pages/info を通じてページを再リクエストします。
  5. 保存された JSON に依然として悪意のある URL が含まれていることを確認します。
  6. HTML 形式で同じページをリクエストし、サーバーが href が依然として javascript:... であるアンカーを返すことを確認します。
  7. UI で、閲覧者がレンダリングされたアタッチメントアクションをクリックすると、Docmost オリジンでペイロードが実行されます。

最小限の悪意のあるコンテンツは次のとおりです:

root@kitploit:~
{
  "pageId": "<pageId>",
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "attachment",
        "attrs": {
          "url": "javascript:alert(document.domain)",
          "name": "policy.pdf",
          "mime": "application/pdf",
          "size": 1
        }
      }
    ]
  },
  "operation": "replace",
  "format": "json"
}

私のテストから観察されたライブ結果は次のとおりです:

  • API は悪意のあるアタッチメントノードを変更せずに受け入れました
  • 保存されたページ ID は 019d18cf-4212-70b0-894a-fe20080fb0f1 でした
  • POST /api/pages/info は次の内容で保存された JSON を返しました:
root@kitploit:~
"url": "javascript:alert(document.domain)"
  • POST /api/pages/info を format: "html" で実行すると、次の HTML が返されました:
root@kitploit:~
<div data-type="attachment" data-attachment-url="javascript:alert(document.domain)" data-attachment-name="policy.pdf" data-attachment-mime="application/pdf" data-attachment-size="1"><a href="javascript:alert(document.domain)" class="attachment" target="blank">policy.pdf</a></div>

この HTML 応答が重要な証拠です。

「ブラウザが何か面白いことをするかもしれない」という曖昧な主張に頼る必要はありませんでした。

アプリケーション自体が正確な実行可能シンクをレンダリングしました。

ユーザーがそのアタッチメントリンク/アイコンをクリックすると、ブラウザは javascript: URL をそのページを作成したオリジンで実行します。


PoC がこのように選択された理由

エディタ駆動の XSS の場合、スクリーンショットだけでは弱い証拠です。

症状は示しますが、境界の失敗は示しません。

そのため、PoC を2つの明示的なチェックポイントで構成しました:

  1. ストレージの証明
  2. レンダリングされたシンクの証明

ストレージの証明は、サーバーが危険なスキームを受け入れ、保持したことを示しました。

レンダリングされたシンクの証明は、アプリケーションがその保存された値を次のように戻したことを示しました:

root@kitploit:~
<a href="javascript:...">

この分割が重要です。

製品が危険な入力を保存しても、すべてのシンクの前にそれを無効化する場合、強化のギャップはあるかもしれませんが、必ずしもライブの XSS があるわけではありません。

製品が危険な入力を保存し、後にそれを実際の実行シンクにレンダリングする場合、完全な脆弱性チェーンがあります。

これがここで起こったことです。


修正の分析

修正は v0.71.0 で提供され、アタッチメント URL に URL サニタイズを適用することで、レンダリングされた悪用パスに対処しました。

アタッチメント拡張は現在 sanitizeUrl をインポートして使用しており、以下を含みます:

  • パース中の data-attachment-url のサニタイズ
  • レンダリング中の data-attachment-url のサニタイズ
  • アンカー href のサニタイズ

概念的には、パッチはアタッチメントノードを次のように変更しました:

  • 生のアタッチメント URL を信頼する
  • 生のアタッチメント URL を出力する

から:

  • アタッチメント URL がレンダリングノードの一部になる前に正規化する

クライアント側のヘルパー getFileUrl() も更新され、未知のスキームがそのまま通過しなくなりました。 パッチ適用バージョンでは、フォールバックパスは sanitizeUrl(src) を返し、src をそのまま返すのではなくなります。

これは修正の重要な部分です。なぜなら、脆弱な設計には2つの補強的な問題があったからです:

  • ノードが生の href をレンダリングした
  • クライアントのフォールバックが未知のスキームを受け入れ可能として扱った

パッチは両方の前提を削除しました。

これはライブの XSS パスに対する良い修正でした。なぜなら、アタッチメント URL 処理をエディタの他のセキュリティモデルと再調整したからです。

とはいえ、より広範な強化の教訓があります:

ここではクライアント側またはレンダリング時のサニタイズが必要ですが、ページ作成/更新時に危険なスキームをサーバー側で拒否することは、さらに強力な不変条件になります。

最も安全な長期モデルは次のとおりです:

  • 取り込み時に明らかに危険なスキームを拒否する
  • レンダリング境界で再度サニタイズする

リッチコンテンツシステムでは多層防御が重要です。


重要なリグレッションケース

長期的なカバレッジのために、最も重要なケースは次のとおりです:

  • attachment.attrs.url = "javascript:..." を含む JSON ページ更新
  • data-attachment-url="javascript:..." を含む HTML インポート
  • アタッチメントレンダリングは決して href="javascript:..." を出力してはならない
  • クライアントフォールバックヘルパーは未知の実行可能スキームを変更せずに返してはならない
  • アタッチメントノードと通常のリンクノードは同等の URL スキームポリシーを共有すべき
  • /api/files/... や /files/... などの安全な内部アタッチメントパスは引き続き正常に動作する必要がある

重要なポイントは一貫性です。

通常のリンクがサニタイズされているが、カスタムの URL 保持ノードがサニタイズされていない場合、エディタは単一の URL セキュリティポリシーを実際には持っていません。

断片化されたものを持っており、そこに XSS バグが存在します。


深刻度と分類

公開されたアドバイザリはこの問題を次のように分類しています:

  • CWE-79: Web ページ生成時の入力の不適切な無害化
  • CVSS v3.1:
root@kitploit:~
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N

これは 7.6 / High になります。

これは妥当な分類です。

重要な特性は次のとおりです:

  • 低い攻撃者権限要件
  • 保存されたペイロード
  • Docmost オリジンでの実行
  • スコープの変更
  • スクリプトが被害者から見えるアプリ内データにアクセスできるため、機密性への影響は重要

ユーザーの相互作用は依然として必要です。被害者はアタッチメントリンク/アイコンをアクティブにする必要があるためです。 そのため UI:R は正しいです。

しかし、その相互作用が発生すると、セキュリティ境界ははるか以前にすでに失敗しています: アプリケーションは危険なスキームを保存し、それを実行シンクにレンダリングしました。


開示

私は GitHub Security Advisories を通じて非公開で問題を報告しました:

  • 根本原因分析
  • ライブ HTTP PoC
  • 保存された JSON 証拠
  • レンダリングされた HTML シンク証拠
  • 固定された使い捨てテストラボ

問題は受理され、CVE-2026-34212 が割り当てられ、2026年4月14日 に公開されました。

公開アドバイザリには現在:

  • 影響を受けるバージョン: 0.70.3
  • 修正バージョン: 0.71.0

私のライブ検証は v0.70.3 で実行され、公開された脆弱なバージョンと一致しました。


このバグが実際に教えること

主な教訓は単に「URL をサニタイズする」ではありません。

誰もがすでにそれを知っています。

より興味深い教訓は次のとおりです:

アプリケーションに安全な URL 保持ノードタイプと安全でない URL 保持ノードタイプがある場合、安全でない方が実際のポリシーです。

リッチテキストシステムは、セキュリティレビューよりも速くカスタム拡張を蓄積することがよくあります。

これにより、まさにこの種の非対称性が生まれます:

  • 標準のリンクパスは強化されている
  • アタッチメントパスは「内部」または「特別なもの」として扱われる
  • 特別なパスは静かに、より簡単な XSS シンクになる

このバグはまた、スキーマ検証だけでは不十分である理由を示しています。

jsonToNode() はコンテンツが構造的に有効な ProseMirror データであることを検証しました。 コンテンツがレンダリングしても安全であることを証明しませんでした。

これらは異なる質問です。

セキュリティレビューは、これらの質問を分けて考えるとはるかに鋭くなります:

  • このコンテンツは構造的に有効か?
  • このコンテンツは保存しても安全か?
  • このコンテンツはすべてのシンクでレンダリングしても安全か?

アタッチメントノードは最初の質問を通過し、3番目の質問に失敗しました。

それが、保存されたコンテンツのバグが、他の点では適切に構造化されたエディタパイプライン内で生き残る方法です。


重要なポイント

  • Docmost はページコンテンツ内の生のアタッチメントノード URL を受け入れました。
  • サーバー側のページ検証は ProseMirror スキーマの形状をチェックしましたが、URL スキームの安全性はチェックしませんでした。
  • 脆弱なアタッチメントノードは、data-attachment-url とアンカー href を攻撃者が制御する入力から直接レンダリングしました。
  • クライアントヘルパー getFileUrl() は、未知のスキームを変更せずに返しました。
  • 通常のリンクノードはすでに javascript: をブロックしていましたが、アタッチメントノードはブロックしませんでした。
  • 低権限の編集者は、ペイロードを一度仕込めば、後から閲覧するユーザーを標的にできました。
  • ライブ PoC は、保存された永続性とレンダリングされた実行可能シンクの両方を証明しました。
  • v0.71.0 の修正では、アタッチメントノードとクライアントフォールバックパスに sanitizeUrl 処理を追加しました。

最後に

この脆弱性はブラウザの癖に関するものではありません。

アプリケーション自身の URL 安全仮定をバイパスするカスタムコンテンツノードに関するものでした。

Docmost は攻撃者が制御するアタッチメント URL を受け入れ、ストレージを通じて保持し、その後アプリケーションオリジン内のライブアンカーにレンダリングしました。

それが CVE-2026-34212 になった理由です。

v0.71.0 のパッチはアクティブな XSS パスをきれいに閉じましたが、より広範な教訓は保持する価値があります:

エディタが多用されるアプリケーションでは、URL を保持できるカスタムノードはそれぞれ独自のセキュリティ境界であり、そのようにレビューされる必要があります。

ツールをダウンロード