## https://sploitus.com/exploit?id=3329EA2C-DB40-5CEB-B3AC-056C2F79EC6B
# 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](https://github.com/Significant-Gravitas/AutoGPT/security/advisories/GHSA-q58p-v9r9-7gqj)
- CVE: [CVE-2026-30950](https://nvd.nist.gov/vuln/detail/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.
## 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:
```py
# 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:
```py
# 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:
```py
# 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 ` **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:
```bash
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/`.