
POC para CVE-2026-30950 que permite sequestro de sessão no AutoGpt
Demonstração de prova de conceito para o CVE-2026-30950, um IDOR autenticado (Autorização Ausente) na Plataforma AutoGPT que permite que qualquer usuário logado reassine — e assim sequestre — a sessão de chat de qualquer outro usuário através de uma única requisição PATCH, sem acesso prévio à sessão.
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)autogpt-platform-backend >= 0.6.36A API de sessão de chat expõe uma rota que permite que um usuário associe sua própria
conta a um registro de sessão. A rota é protegida por autenticação JWT
mas não verifica se o chamador é o proprietário atual da sessão
que está sendo modificada — apenas que é algum usuário autenticado. Uma chamada
direta com o session_id da vítima e o JWT do atacante transfere
a propriedade da sessão para o atacante.
A vulnerabilidade tem três camadas e a bypass é estabelecida em cada uma delas:
# 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"}
A rota aceita qualquer chamador autenticado. Ela repassa para o serviço:
# 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
O serviço deliberadamente passa user_id=None para o acessor de dados,
em vez de encaminhar o ID do usuário chamador. O acessor de dados então
trata None como modo administrador e ignora o filtro de propriedade:
# 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
Quando user_id is None, a conjunção é curto-circuitada e a verificação
de incompatibilidade nunca é realizada — qualquer sessão é retornada para qualquer
chamador. O serviço então sobrescreve session.user_id com o ID do chamador e
armazena o resultado em cache no Redis, de modo que as consultas subsequentes pelo
proprietário original são rejeitadas pela mesma verificação de incompatibilidade que
foi contornada.
Qualquer instância da Plataforma AutoGPT executando autogpt-platform-backend
>= 0.6.36 e < 0.6.51 com o recurso de chat (copilot) habilitado
é explorável. A exploração requer:
Impacto por sessão, escalando com cada sessão sequestrada:
A pontuação CVSS (C:H / I:N / A:L) reflete o escopo por sessão: alto impacto na confidencialidade das sessões sequestradas, nenhum dano à integridade dos conteúdos das mensagens pré-existentes, e um impacto na disponibilidade (bloqueio) para o proprietário legítimo.
Este repositório disponibiliza um POC, cobrindo o gatilho direto: uma
única requisição PATCH a partir do JWT de um atacante contra um
session_id conhecido. O script de configuração cria dois usuários (atacante + vítima)
em uma pilha AutoGPT recém-inicializada e verifica as pré-condições
antes de declarar prontidão.
setup/ — Ambiente Docker Compose.
setup.sh clona o AutoGPT na última tag vulnerável
(autogpt-platform-beta-v0.6.50) para setup/AutoGPT-src/, sobe
os serviços mínimos do docker-compose.yml original
(rest_server, copilot_executor, database_manager, migrate,
além de suas dependências transitivas: Postgres, Redis, RabbitMQ, Supabase
Kong + GoTrue), cria dois usuários de teste e verifica se o
endpoint vulnerável está roteado.teardown.sh desce a pilha e limpa os volumes.pocs/session_hijack.py — Exploit autocontido. Faz login como
vítima e atacante, cria uma sessão como vítima, confirma que o
atacante não tem acesso de leitura pré-exploração, dispara a única
requisição PATCH, então prova que a propriedade foi transferida e a
vítima está bloqueada.
Pré-requisitos: Docker, Git, Python 3.10+ e uv.
cd setup
./setup.sh
A primeira execução clona o AutoGPT na tag vulnerável e constrói a imagem do backend (~3-5 min). Execuções subsequentes são rápidas. Quando a configuração termina, ela imprime a invocação do POC pronta para copiar.
Execute o 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!'
Por que as portas altas? O compose original codifica nomes de contêineres (
supabase-db,rabbitmq, ...) e publica portas padrão (5432, 8000, ...). Para coexistir com outras pilhas Supabase / Postgres / RabbitMQ que o usuário já possa estar executando,setup/docker-compose.override.ymlrenomeia os contêineres, isola as redes sob um nome de projeto único (autogpt-cve-2026-30950) e eleva as duas portas externamente expostas para a faixa 58000. Os outros serviços internos vinculam-se apenas à rede docker do projeto.
A saída esperada termina com:
[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED
Desmontagem:
cd setup
./teardown.sh
O código-fonte do AutoGPT clonado em setup/AutoGPT-src/ é preservado entre
desmontagens para que reexecuções não precisem baixar novamente. Para removê-lo
completamente, delete setup/AutoGPT-src/.