
POC para CVE-2026-30950 que permite el secuestro de sesión en AutoGpt
Demostración de prueba de concepto para CVE-2026-30950, un IDOR autenticado (falta de autorización) en AutoGPT Platform que permite a cualquier usuario con sesión iniciada reasignar — y por lo tanto secuestrar — la sesión de chat de cualquier otro usuario mediante una única petición PATCH, sin acceso previo a la sesión.
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)autogpt-platform-backend >= 0.6.36La API de sesiones de chat expone una ruta que permite al usuario vincular su propia cuenta a un registro de sesión. La ruta está protegida por autenticación JWT, pero no comprueba que el llamante sea el propietario actual de la sesión que se está modificando — solo que es algún usuario autenticado. Una llamada directa con el session_id de la víctima y el JWT del atacante transfiere la propiedad de la sesión al atacante.
La vulnerabilidad tiene tres capas y la omisión se logra en cada una de ellas:
# 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"}
La ruta acepta a cualquier llamante autenticado y delega en el servicio:
# 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
El servicio pasa deliberadamente user_id=None al acceso a datos, en lugar de reenviar el ID de usuario del llamante. El acceso a datos trata entonces None como modo administrador y se salta el filtro de propiedad:
# 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
Cuando user_id is None, la conjunción se cortocircuita y la comprobación de coincidencia nunca se realiza: se devuelve cualquier sesión a cualquier llamante. El servicio sobrescribe entonces session.user_id con el ID del llamante y guarda en caché el resultado en Redis, de modo que las consultas posteriores del propietario original son rechazadas por la misma comprobación de coincidencia que se acaba de omitir.
Cualquier instancia de AutoGPT Platform que ejecute autogpt-platform-backend >= 0.6.36 y < 0.6.51 con la función de chat (copiloto) habilitada es explotable. El exploit requiere:
Impacto por sesión, que escala con cada sesión secuestrada:
user_id del atacante; las lecturas posteriores de la víctima no superan la comprobación de propiedad y devuelven 404.La puntuación CVSS (C:H / I:N / A:L) refleja el alcance por sesión: alto impacto de confidencialidad en las sesiones secuestradas, ningún daño de integridad sobre el contenido de los mensajes preexistentes e impacto de disponibilidad (bloqueo) sobre el propietario legítimo.
Este repositorio incluye un POC, que cubre el desencadenante directo: una única petición PATCH realizada con el JWT del atacante contra un session_id conocido. El script de configuración crea dos usuarios (atacante + víctima) en una pila de AutoGPT recién inicializada y verifica las condiciones previas antes de declararla lista.
setup/ — Entorno Docker Compose.
setup.sh clona AutoGPT en la última etiqueta vulnerable (autogpt-platform-beta-v0.6.50) en setup/AutoGPT-src/, levanta los servicios mínimos del docker-compose.yml original (rest_server, copilot_executor, database_manager, migrate, más sus dependencias transitivas: Postgres, Redis, RabbitMQ, Supabase Kong + GoTrue), crea dos usuarios de prueba y verifica que el endpoint vulnerable está enrutado.teardown.sh detiene la pila y elimina los volúmenes.pocs/session_hijack.py — Exploit autocontenido. Inicia sesión como víctima y como atacante, crea una sesión como la víctima, confirma que el atacante no tiene acceso de lectura antes del exploit, lanza la única petición PATCH y demuestra que la propiedad se ha transferido y que la víctima queda bloqueada.
Requisitos previos: Docker, Git, Python 3.10+ y uv.
cd setup
./setup.sh
La primera ejecución clona AutoGPT en la etiqueta vulnerable y construye la imagen del backend (~3-5 min). Las ejecuciones posteriores son rápidas. Cuando la configuración termina, imprime la invocación del POC lista para copiar y pegar.
Ejecuta el 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 qué los puertos altos? El compose original fija los nombres de los contenedores (
supabase-db,rabbitmq, ...) y publica los puertos por defecto (5432, 8000, ...). Para coexistir con cualquier otra pila de Supabase / Postgres / RabbitMQ que el usuario ya esté ejecutando,setup/docker-compose.override.ymlrenombra los contenedores, aísla las redes bajo un nombre de proyecto único (autogpt-cve-2026-30950) y sube los dos puertos expuestos externamente al rango 58000. Los demás servicios internos se vinculan solo a la red docker del proyecto.
La salida esperada termina con:
[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED
Desmonta el entorno:
cd setup
./teardown.sh
El código fuente de AutoGPT clonado en setup/AutoGPT-src/ se conserva entre desmontajes, de modo que las re-ejecuciones no lo vuelven a descargar. Para eliminarlo por completo, borra setup/AutoGPT-src/.