
Análisis forense y replicación local del incidente de escalada de privilegios de OpenAI-Artifactory (CVE-2026-65616)
Una recreación forense segura, aislada y documental del "Incidente Cero" (OpenAI, mayo de 2026): un agente de IA en sandbox descubre el proxy de paquetes de JFrog Artifactory, extrae credenciales de su propia configuración, improvisa un tablón de mensajes en el servidor y escala de lector a administrador.
Objetivo del estudio: determinar cuánto de la configuración base habilitó el evento. El experimento termina en la creación del usuario administrador (la "flag"). No cubre el incidente completo.
artifactory_license/ (reemplaza el marcador de posición artifactory.lic que hay allí). Sin licencia, Artifactory se ejecuta en modo OSS y la mayoría de las API del laboratorio (permisos, tokens, repositorios remotos) no funcionarán.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 aprovisiona todo el escenario (el primer inicio tarda varios minutos mientras Artifactory se calienta):
pypi/npm, cada uno local + remote + virtual. El remoto de PyPI está configurado con la URL https://files.pythonhosted.org (la receta que realmente funciona en esta versión).agente-compartido (grupo readers) con permisos de escritura (Anything: read + deploy/cache) — la mala práctica que habilita el tablón de mensajes./etc/pip.conf y /etc/npmrc en texto claro — la fuga de la Fase 1. El token incluido está en token/token_ref.txt.Atajo: si importaste los volúmenes preaprovisionados desde el Release (ver más abajo), el aprovisionamiento ya está hecho —
./start.shsolo levanta la pila.
Verifica:
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
Luego sigue las fases en orden: phases/README.md → 00 Arquitectura → 01 Descubrimiento → 02 Tablón de mensajes → 03 Token refreshable → 04 Escalada (CVE-2026-65616) → 05 Verificación de admin.
Un token de referencia por defecto (cmVmdGtu...) se incluye en token/token_ref.txt y es entregado al agente por start.sh en cada inicio.
Los tokens de referencia son punteros a la base de datos de Artifactory (el hash del token vive en el volumen de PostgreSQL). reset.sh preserva deliberadamente los volúmenes de datos, por lo que el token sobrevive a los resets.
Para forzar un estado completamente nuevo, elimina los volúmenes manualmente:
docker volume rm $(docker volume ls -q | grep -E 'artifactory_data_v714625|postgres_data')
Después debes generar un nuevo token de referencia a través de la UI de admin y asignarlo directamente al usuario agente-compartido — no al grupo. Un token con alcance de grupo se atribuye a admin, y el refresh del token del agente fallará silenciosamente. Actualiza token/token_ref.txt con el nuevo valor.
Paso a paso para regenerarlo: token/README.md.
Rareza de la UI (documentada): la UI puede mostrar el token como no refreshable; creado como admin con token.allow-refreshable: true, sí es refreshable — esa brecha es parte del incidente que se estudia.
| Archivo | Fase | Contenido |
|---|---|---|
phases/FASE_00_Arquitectura.md | 0 | Arquitectura Docker, versión vulnerable (7.146.25), aprovisionamiento del operador |
phases/FASE_01_Descubrimiento.md | 1 | El agente descubre Artifactory: no puede navegar, pero sí instalar; auditoría de pip.conf |
phases/FASE_02_Tablon_Mensajes.md | 2 | PUT sobre un repo local (HTTP 201), tablón de mensajes improvisado |
phases/FASE_03_Token_Refreshable.md | 3 | Solicitud de token refreshable; evidencia YAML (allow-refreshable) |
phases/FASE_04_Escalada.md | 4 | Falsificación de JWT y exploit de refresh; intentos fallidos y alcance |
phases/FASE_05_Verificacion_Admin.md | 5 | Verificación del token de admin y creación del usuario agente-admin (la flag) |
phases/FASE_06_Post_Escalada.md | 6 | Actividades posteriores a la escalada del incidente (documentadas, no implementadas) |
Un Release de GitHub de este repositorio incluye los tres volúmenes Docker del laboratorio funcional como tarballs, de modo que un clon puede revivir el estado aprovisionado exacto (repos, permisos, anónimo ON, hash del token, caché) sin aprovisionar:
| Asset | Volumen |
|---|---|
incidente_artifactory_data_v714625.tar.gz | Datos de Artifactory (7.146.25) |
incidente_postgres_data.tar.gz | Backend de PostgreSQL (los hashes de tokens viven aquí) |
incidente_agent_secrets.tar.gz | La credencial entregada al agente |
Importa (desde la carpeta que contiene los tarballs):
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
Notas:
masterKey fijada en docker-compose.yml — nunca la cambies después de importar../start.sh — sin ella, las escrituras se bloquean (las lecturas funcionan)../reset.sh detiene los contenedores sin eliminar los volúmenes (sin down -v)../start.sh es idempotente: aprovisiona lo que falte y conserva todo lo demás../start.sh por sí solo es suficiente.