
AutoGptにおけるセッションハイジャックを可能にするCVE-2026-30950のPoC
CVE-2026-30950 の概念実証デモンストレーションです。AutoGPT プラットフォームにおける認証された IDOR(認可の欠如)であり、ログイン済みの任意のユーザーが、事前にセッションにアクセスすることなく、1 回の PATCH リクエストで他のユーザーのチャットセッションを再割り当て(つまり乗っ取り)できる脆弱性です。
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)autogpt-platform-backend >= 0.6.36チャットセッション API は、ユーザーが自身のアカウントをセッションレコードにアタッチできるルートを公開しています。このルートは JWT 認証によって保護されていますが、変更対象のセッションを現在呼び出し元が所有しているかのチェックは一切行われず、認証されたユーザーであることのみが確認されます。被害者の session_id と攻撃者の JWT を用いて直接呼び出すと、セッションの所有権が攻撃者に移ります。
この脆弱性には3つの層があり、それぞれの層で回避が成立します。
# autogpt_platform/backend/backend/api/features/chat/routes.py:753-776
@router.patch(
"/sessions/{session_id}/assign-user",
dependencies=[Security(auth.requires_user)],
status_code=200,
)
async def session_assign_user(
session_id: str,
user_id: Annotated[str, Security(auth.get_user_id)],
) -> dict:
await chat_service.assign_user_to_session(session_id, user_id)
return {"status": "ok"}
このルートは認証された呼び出し元をすべて受け入れます。その後、サービスに処理を委譲します。
# autogpt_platform/backend/backend/copilot/service.py:291-303
async def assign_user_to_session(session_id: str, user_id: str) -> ChatSessionInfo:
session = await get_chat_session(session_id, None) # ← user_id=None
if not session:
raise NotFoundError(f"Session {session_id} not found")
session.user_id = user_id
session = await upsert_chat_session(session)
return session
サービスは、呼び出し元のユーザー ID を転送する代わりに、意図的に user_id=None をデータアクセサに渡します。データアクセサは None を管理者モードとして扱い、所有権フィルタをスキップします。
# autogpt_platform/backend/backend/copilot/model.py:355-366
session = await _get_session_from_cache(session_id)
if session:
if user_id is not None and session.user_id != user_id:
logger.warning(f"Session {session_id} user id mismatch")
return None
return session
user_id is None の場合、条件式は短絡され、不一致チェックは実行されません。その結果、任意のセッションが任意の呼び出し元に返されます。その後、サービスは session.user_id を呼び出し元の ID で上書きし、結果を Redis にキャッシュするため、元の所有者によるその後の参照は、先ほど回避されたのと同じ不一致チェックによって拒否されます。
autogpt-platform-backend の >= 0.6.36 かつ < 0.6.51 を実行し、チャット(copilot)機能が有効になっているすべての AutoGPT プラットフォームインスタンスが悪用可能です。悪用には以下が必要です。
乗っ取られたセッションごとに影響が拡大します。
CVSS スコア(C:H / I:N / A:L)はセッション単位の範囲を反映しています。乗っ取られたセッションに対する機密性への影響は高く、既存のメッセージ内容への整合性への損傷はなく、正当な所有者に対する可用性への影響(ロックアウト)があります。
このリポジトリには 1 つの POC が含まれており、直接的なトリガー(既知の session_id に対する攻撃者の JWT からの単一の PATCH リクエスト)をカバーしています。セットアップスクリプトは、新しくブートストラップされた AutoGPT スタック内に 2 人のユーザー(攻撃者 + 被害者)を作成し、準備完了を宣言する前に前提条件を検証します。
setup/ — Docker Compose 環境。
setup.sh は、最後の脆弱なタグ(autogpt-platform-beta-v0.6.50)で AutoGPT を setup/AutoGPT-src/ にクローンし、アップストリームの docker-compose.yml から最小限のサービス(rest_server、copilot_executor、database_manager、migrate、およびそれらの推移的依存関係:Postgres、Redis、RabbitMQ、Supabase Kong + GoTrue)を起動し、2 人のテストユーザーを作成し、脆弱性のあるエンドポイントがルーティングされていることを確認します。teardown.sh はスタックを停止し、ボリュームを削除します。pocs/session_hijack.py — 自己完結型のエクスプロイト。被害者と攻撃者としてログインし、被害者としてセッションを作成し、攻撃者が事前に読み取りアクセスできないことを確認し、単一の PATCH リクエストを発行し、所有権が移譲され被害者がロックアウトされたことを証明します。
前提条件: Docker、Git、Python 3.10+、および uv。
cd setup
./setup.sh
初回実行時は、脆弱なタグで AutoGPT をクローンし、バックエンドイメージをビルドします(約3~5分)。以降の実行は高速です。セットアップが完了すると、コピーして実行可能な POC 呼び出しが表示されます。
POC を実行:
uv run --no-project --with requests \
pocs/session_hijack.py \
--api-url http://localhost:58006 \
--auth-url http://localhost:58000 \
--attacker-email [email protected] \
--attacker-password 'Attacker123!' \
--victim-email [email protected] \
--victim-password 'Victim123!'
なぜハイポートなのか? アップストリームの compose はコンテナ名(
supabase-db、rabbitmq、...)をハードコードし、デフォルトポート(5432、8000、...)を公開しています。ユーザーが既に実行している可能性のある他の Supabase / Postgres / RabbitMQ スタックと共存するために、setup/docker-compose.override.ymlはコンテナの名前を変更し、ネットワークを一意のプロジェクト名(autogpt-cve-2026-30950)で分離し、外部に公開する 2 つのポートを 58000 番台に上げています。他の内部サービスはプロジェクトの Docker ネットワークにのみバインドされます。
期待される出力の末尾:
[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED
停止:
cd setup
./teardown.sh
setup/AutoGPT-src/ にクローンされた AutoGPT ソースは、停止後も保持されるため、再実行時にダウンロードが再実行されることはありません。完全に削除するには、setup/AutoGPT-src/ を削除してください。