
POC pour CVE-2026-30950 qui permet le détournement de session dans AutoGpt
Démonstration de preuve de concept pour CVE-2026-30950, une IDOR authentifiée (Absence d'autorisation) dans la plateforme AutoGPT qui permet à tout utilisateur connecté de réattribuer — et donc détourner — la session de discussion de tout autre utilisateur via une seule requête PATCH, sans accès préalable à la session.
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)autogpt-platform-backend >= 0.6.36L'API de session de discussion expose une route qui permet à un utilisateur d'attacher son propre compte à un enregistrement de session. La route est protégée par une authentification JWT mais n'effectue aucune vérification que l'appelant possède actuellement la session en cours de modification — seulement qu'il est un utilisateur authentifié. Un appel direct avec le session_id de la victime et le JWT de l'attaquant transfère la propriété de la session à l'attaquant.
La vulnérabilité comporte trois couches et le contournement est établi à chacune d'elles :
# 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 route accepte tout appelant authentifié. Elle délègue au 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
Le service passe délibérément user_id=None à l'accesseur de données, au lieu de transmettre l'ID utilisateur de l'appelant. L'accesseur de données traite alors None comme mode admin et ignore le filtre de propriété :
# 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
Lorsque user_id is None, la conjonction court-circuite et la vérification de non-concordance n'est jamais effectuée — toute session est renvoyée à tout appelant. Le service écrase ensuite session.user_id avec l'ID de l'appelant et met en cache le résultat dans Redis, de sorte que les recherches ultérieures par le propriétaire d'origine sont rejetées par la même vérification de non-concordance qui vient d'être contournée.
Toute instance de la plateforme AutoGPT exécutant autogpt-platform-backend >= 0.6.36 et < 0.6.51 avec la fonctionnalité de chat (copilot) activée est exploitable. L'exploit nécessite :
Impact par session, qui s'aggrave avec chaque session détournée :
Le score CVSS (C:H / I:N / A:L) reflète la portée par session : impact de confidentialité élevé sur les sessions détournées, aucun dommage à l'intégrité des contenus des messages préexistants, et un impact de disponibilité (verrouillage) sur le propriétaire légitime.
Ce dépôt contient un POC, couvrant le déclencheur direct : une seule requête PATCH à partir d'un JWT d'attaquant contre un session_id connu. Le script de configuration crée deux utilisateurs (attaquant + victime) dans une pile AutoGPT fraîchement amorcée et vérifie les préconditions avant de déclarer le système prêt.
setup/ — Environnement Docker Compose.
setup.sh clone AutoGPT au dernier tag vulnérable (autogpt-platform-beta-v0.6.50) dans setup/AutoGPT-src/, démarre les services minimaux du docker-compose.yml amont (rest_server, copilot_executor, database_manager, migrate, plus leurs dépendances transitives : Postgres, Redis, RabbitMQ, Supabase Kong + GoTrue), crée deux utilisateurs de test et vérifie que le point de terminaison vulnérable est routé.teardown.sh arrête la pile et nettoie les volumes.pocs/session_hijack.py — Exploit autonome. Se connecte en tant que victime et attaquant, crée une session en tant que victime, confirme que l'attaquant n'a pas d'accès en lecture avant l'exploit, envoie la seule requête PATCH, puis prouve que la propriété a été transférée et que la victime est verrouillée.
Prérequis : Docker, Git, Python 3.10+ et uv.
cd setup
./setup.sh
La première exécution clone AutoGPT au tag vulnérable et construit l'image backend (~3-5 min). Les exécutions suivantes sont rapides. Une fois la configuration terminée, elle imprime l'invocation du POC prête à être copiée.
Exécuter le 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!'
Pourquoi les ports élevés ? La composition amont (upstream) code en dur les noms des conteneurs (
supabase-db,rabbitmq, ...) et publie les ports par défaut (5432, 8000, ...). Pour coexister avec d'autres piles Supabase / Postgres / RabbitMQ que l'utilisateur pourrait déjà exécuter,setup/docker-compose.override.ymlrenomme les conteneurs, isole les réseaux sous un nom de projet unique (autogpt-cve-2026-30950), et augmente les deux ports exposés à l'extérieur dans la plage 58000. Les autres services internes ne se lient qu'au réseau Docker du projet.
La sortie attendue se termine par :
[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED
Arrêt et nettoyage :
cd setup
./teardown.sh
Le code source cloné d'AutoGPT dans setup/AutoGPT-src/ est conservé lors des arrêts afin que les réexécutions ne téléchargent pas à nouveau. Pour le supprimer complètement, supprimez setup/AutoGPT-src/.