Skip to content

ZeroPathAI/autogpt-CVE-2026-30950-poc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

AutoGPT CVE-2026-30950 POC

Proof-of-concept demonstration for CVE-2026-30950, an authenticated IDOR (Missing Authorization) in the AutoGPT Platform that lets any logged-in user reassign — and thus hijack — any other user's chat session via a single PATCH request, with no prior access to the session.

  • GHSA: GHSA-q58p-v9r9-7gqj
  • CVE: CVE-2026-30950
  • CVSS 3.1: 7.1 / High (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)
  • Affected: autogpt-platform-backend >= 0.6.36
  • Fixed in: 0.6.51
  • Discovered by ZeroPath. Our technical blog contains a full write up with more details!

Vulnerability

Missing ownership check on session-assign endpoint (CVE-2026-30950, CWE-862)

The chat-session API exposes a route that lets a user attach their own account to a session record. The route is gated by JWT authentication but performs no check that the caller currently owns the session being modified — only that they are some authenticated user. A direct call with the victim's session_id and the attacker's JWT transfers ownership of the session to the attacker.

The vulnerability has three layers and the bypass is established at each one:

# 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"}

The route accepts any authenticated caller. It hands off to the service:

# 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

The service deliberately passes user_id=None to the data accessor, instead of forwarding the caller's user ID. The data accessor then treats None as admin-mode and skips the ownership filter:

# 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

When user_id is None, the conjunction short-circuits and the mismatch check is never performed — any session is returned to any caller. The service then overwrites session.user_id with the caller's ID and caches the result in Redis, so subsequent lookups by the original owner are rejected by the same mismatch check that was just bypassed.

When is a server exploitable?

Any AutoGPT Platform instance running autogpt-platform-backend >= 0.6.36 and < 0.6.51 with the chat (copilot) feature enabled is exploitable. The exploit requires:

  1. An authenticated session. The attacker must hold any valid Supabase JWT — a standard signed-up user account is sufficient.
  2. A target session ID. Session IDs are UUIDs but they appear in URLs and logs; any leak — referer headers, shared links, screen shares, server logs, support tickets — is enough.

What can an attacker do?

Per-session impact, escalating with each hijacked session:

  • Read all messages in the hijacked session. Chat sessions in AutoGPT contain conversation history with the agent, including tool invocations, file references, and any sensitive data the user pasted into the chat.
  • Lock the legitimate owner out. After the assignment, the Redis cache holds the attacker's user_id; the victim's subsequent reads fail the ownership check and return 404.
  • Pivot through the session. Whatever the session was authorized to do for the victim, the attacker can now do — submit follow-up messages, trigger agent actions, exfiltrate workspace contents attached to the session.

The CVSS score (C:H / I:N / A:L) reflects the per-session scope: high confidentiality impact on hijacked sessions, no integrity damage to pre-existing message contents, and an availability impact (lockout) on the legitimate owner.

POC scope

This repository ships one POC, covering the direct trigger: a single PATCH request from an attacker's JWT against a known session_id. The setup script creates two users (attacker + victim) in a freshly-bootstrapped AutoGPT stack and verifies the preconditions before declaring ready.

Repository contents

  • setup/ — Docker Compose environment.

    • setup.sh clones AutoGPT at the last vulnerable tag (autogpt-platform-beta-v0.6.50) into setup/AutoGPT-src/, brings up the minimum services from upstream's docker-compose.yml (rest_server, copilot_executor, database_manager, migrate, plus their transitive deps: Postgres, Redis, RabbitMQ, Supabase Kong + GoTrue), creates two test users, and verifies the vulnerable endpoint is routed.
    • teardown.sh brings down the stack and wipes volumes.
  • pocs/session_hijack.py — Self-contained exploit. Logs in as victim and attacker, creates a session as the victim, confirms the attacker has no pre-exploit read access, fires the single PATCH request, then proves ownership has transferred and the victim is locked out.

Instructions

Prerequisites: Docker, Git, Python 3.10+, and uv.

cd setup
./setup.sh

The first run clones AutoGPT at the vulnerable tag and builds the backend image (~3-5 min). Subsequent runs are quick. When setup completes it prints the ready-to-paste POC invocation.

Run the POC:

uv run --no-project --with requests \
    pocs/session_hijack.py \
    --api-url http://localhost:58006 \
    --auth-url http://localhost:58000 \
    --attacker-email attacker@autogpt-poc.local \
    --attacker-password 'Attacker123!' \
    --victim-email victim@autogpt-poc.local \
    --victim-password 'Victim123!'

Why the high ports? Upstream's compose hardcodes container names (supabase-db, rabbitmq, ...) and publishes default ports (5432, 8000, ...). To coexist with any other Supabase / Postgres / RabbitMQ stacks the user might already be running, setup/docker-compose.override.yml renames the containers, isolates the networks under a unique project name (autogpt-cve-2026-30950), and bumps the two externally-exposed ports into the 58000 range. The other internal services bind only to the project's docker network.

Expected output ends with:

[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED

Tear down:

cd setup
./teardown.sh

The cloned AutoGPT source in setup/AutoGPT-src/ is preserved across teardowns so re-runs don't re-download. To wipe it entirely, delete setup/AutoGPT-src/.

About

POC for CVE-2026-30950 which allows session hijacking in AutoGpt

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors