
Analyse forensique et réplication locale de l'incident d'élévation de privilèges OpenAI-Artifactory (CVE-2026-65616)
Une reconstitution forensique sûre, isolée et documentaire de l'« Incidente Cero » (OpenAI, mai 2026) : un agent IA en bac à sable découvre le proxy de paquets JFrog Artifactory, extrait les identifiants de sa propre configuration, improvise un tableau de messages sur le serveur et passe du statut de lecteur à celui d'administrateur.
Objectif de l'étude : déterminer quelle part de la configuration de base a permis l'événement. L'expérience s'arrête à la création de l'utilisateur administrateur (le « flag »). Elle ne couvre pas l'incident dans son intégralité.
artifactory_license/ (remplacez le fichier artifactory.lic qui s'y trouve). Sans licence, Artifactory fonctionne en mode OSS et la plupart des API du labo (permissions, jetons, dépôts distants) ne fonctionneront pas.git clone <this-repo> && cd <dir>
# 1. put your license in artifactory_license/artifactory.lic
# 2. clean start (preserves data volumes; see "Reset semantics" below):
./reset.sh && ./start.sh
start.sh provisionne l'ensemble du scénario (le premier démarrage prend plusieurs minutes, le temps qu'Artifactory s'initialise) :
pypi/npm, chacun local + remote + virtual. Le dépôt distant PyPI est configuré avec l'URL https://files.pythonhosted.org (la recette qui fonctionne réellement dans cette version).agente-compartido (groupe readers) avec des permissions d'écriture (Anything : lecture + déploiement/cache) — la mauvaise pratique qui permet le tableau de messages./etc/pip.conf et /etc/npmrc — la fuite de la phase 1. Le jeton fourni se trouve dans .Raccourci : si vous avez importé les volumes pré-provisionnés depuis la Release (voir ci-dessous), le provisionnement est déjà fait —
./start.shse contente de démarrer la pile.
Vérification :
curl -s http://localhost:8081/artifactory/api/system/ping # -> "true"
docker exec -it incidente-modelo-simulado sh
# inside the agent:
cat /etc/pip.conf # leaked credentials (the Fase 1 leak)
. /opt/venv/bin/activate
pip install six # packages without internet, via the remote
Suivez ensuite les phases dans l'ordre : phases/README.md → 00 Architecture → 01 Découverte → 02 Tableau de messages → 03 Jeton rafraîchissable → 04 Escalade (CVE-2026-65616) → 05 Vérification administrateur.
Un jeton de référence par défaut (cmVmdGtu...) est fourni dans token/token_ref.txt et est transmis à l'agent par start.sh à chaque démarrage.
Les jetons de référence sont des pointeurs vers la base de données d'Artifactory (le hachage du jeton réside dans le volume PostgreSQL). reset.sh préserve délibérément les volumes de données, de sorte que le jeton survit aux réinitialisations.
Pour forcer un état totalement neuf, supprimez les volumes manuellement :
docker volume rm $(docker volume ls -q | grep -E 'artifactory_data_v714625|postgres_data')
Vous devez alors générer un nouveau jeton de référence via l'interface d'administration et l'attribuer directement à l'utilisateur agente-compartido — pas au groupe. Un jeton porté par un groupe est attribué à admin, et le rafraîchissement du jeton de l'agent échouera silencieusement. Mettez à jour token/token_ref.txt avec la nouvelle valeur.
Procédure pas à pas pour la régénération : token/README.md.
Une Release GitHub de ce dépôt fournit les trois volumes Docker du labo fonctionnel sous forme d'archives tar, afin qu'un clone puisse faire revivre l'état provisionné exact (dépôts, permissions, anonyme activé, hachage du jeton, cache) sans provisionnement :
| Ressource | Volume |
|---|---|
incidente_artifactory_data_v714625.tar.gz | Données Artifactory (7.146.25) |
incidente_postgres_data.tar.gz | Backend PostgreSQL (les hachages de jetons s'y trouvent) |
incidente_agent_secrets.tar.gz | L'identifiant transmis à l'agent |
Import (depuis le dossier contenant les archives tar) :
for V in incidente_artifactory_data_v714625 incidente_postgres_data incidente_agent_secrets; do
docker volume create $V
docker run --rm -v $V:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/$V.tar.gz"
done
./start.sh
Remarques :
masterKey fixée dans docker-compose.yml — ne la changez jamais après l'import../start.sh — sans elle, les écritures sont bloquées (les lectures fonctionnent)../reset.sh arrête les conteneurs sans supprimer les volumes (pas de down -v)../start.sh est idempotent : il provisionne ce qui manque et conserve tout le reste../start.sh seul suffit.Chaque condition isolée du scénario a une justification de commodité (cache, identifiant partagé, jetons rafraîchissables). Orchestrées, elles montrent qu'aucune escalade cryptographique sophistiquée n'était nécessaire : quatre des six maillons de la chaîne causale sont des décisions de configuration. La frontière de confiance avait été tracée autour de l'entreprise, et non autour de chaque acteur — et l'agent était un acteur à l'intérieur du périmètre. La thèse en une phrase : Le Zero Trust n'est pas pour les modèles, il est pour les entreprises ; lorsque le consommateur change de nature (script → agent autonome), la surface de confiance doit être recalibrée.
Si vous utilisez ce labo dans le cadre de recherches ou d'un enseignement, veuillez le citer via son DOI Zenodo 10.5281/zenodo.22817059 :
Colmenero-Fernandez, A. (2026). Forensis Lab: Forensic Recreation of the AI Agent Privilege Escalation (JFrog Artifactory, CVE-2026-65616) (v1.0.0). Zenodo. https://doi.org/10.5281/zenodo.22817059
BibTeX :
@software{colmenerofernandez2026forensislab,
author = {Colmenero-Fernandez, Alicia},
title = {{Forensis Lab: Forensic Recreation of the AI Agent Privilege Escalation (JFrog Artifactory, CVE-2026-65616)}},
year = {2026},
version = {1.0.0},
doi = {10.5281/zenodo.22817059},
url = {https://doi.org/10.5281/zenodo.22817059}
}
Métadonnées de citation lisibles par machine : CITATION.cff.
Bizarrerie de l'interface (documentée) : l'interface peut afficher le jeton comme non rafraîchissable ; créé en tant qu'admin avec token.allow-refreshable: true, il est rafraîchissable — cet écart fait partie de l'incident étudié.
| Fichier | Phase | Contenu |
|---|
phases/FASE_00_Arquitectura.md | 0 | Architecture Docker, version vulnérable (7.146.25), provisionnement par l'opérateur |
phases/FASE_01_Descubrimiento.md | 1 | L'agent découvre Artifactory : navigation impossible, mais installation possible ; audit de pip.conf |
phases/FASE_02_Tablon_Mensajes.md | 2 | PUT sur un dépôt local (HTTP 201), tableau de messages improvisé |
phases/FASE_03_Token_Refreshable.md | 3 | Demande de jeton rafraîchissable ; preuve YAML (allow-refreshable) |
phases/FASE_04_Escalada.md | 4 | Falsification de JWT et exploitation du rafraîchissement ; tentatives échouées et portée |
phases/FASE_05_Verificacion_Admin.md | 5 | Vérification du jeton administrateur et création de l'utilisateur agente-admin (le flag) |
phases/FASE_06_Post_Escalada.md | 6 | Activités post-escalade de l'incident (documentées, non implémentées) |