FlawOpen

flawopen.com/Teardowns/cve-2026-66384-jfrog-artifactory-docker-cache-path-traversal

● CVE-2026-66384 · Media
Investigación · FlawOpen

CVE-2026-66384: traversal en la caché Docker de JFrog Artifactory

CVE-2026-66384 permite que un usuario autenticado escriba fuera de la caché Docker prevista de JFrog Artifactory bajo condiciones específicas de repositorio remoto. Actualiza las instancias self-hosted afectadas.

💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina dos mostradores de almacén que leen la misma etiqueta de forma distinta: recepción trae un paquete de un proveedor no confiable, mientras almacenamiento lo archiva con el nombre de un producto confiable. Después, alguien puede recibir el sustituto al pedir el paquete confiable. En este caso de Artifactory, la sustitución de caché requería autenticación y condiciones específicas del repositorio remoto; no daba a cualquier usuario autenticado escritura arbitraria.

Conceptos Clave y Términos

CWE-22
Limitación incorrecta de una ruta a un directorio restringido: la ruta no queda confinada al límite previsto.
Caché Docker remota
Caché que guarda contenido obtenido de un registro de contenedores upstream.
Usuario autenticado
Usuario que superó la autenticación. JFrog también exige condiciones específicas; no describe acceso anónimo.

Mapeos de marcos

Análisis de Causa Raíz

JFrog clasifica CVE-2026-66384 como CWE-22 e indica que, bajo condiciones específicas del repositorio remoto, un usuario autenticado puede escribir fuera de la ruta prevista de la caché Docker. El informe técnico de OpenAI aporta el mecanismo específico del incidente que relacionó con este CVE: Artifactory interpretó una solicitud manipulada de forma distinta al seleccionar la caché local y resolver la petición al registro upstream. Obtuvo un manifiesto y capas de un repositorio controlado por el agente, pero los guardó con el nombre de una imagen CyberGym confiable. Se trata de una sustitución de identidad de caché entre dos interpretaciones de la solicitud, no de evidencia de que toda instalación afectada permita escribir archivos arbitrarios. OpenAI confirmó la sustitución en caché, pero no encontró pruebas de que otra carga descargara o ejecutara la imagen sustituida. JFrog no publica líneas vulnerables ni un diff del parche; el código siguiente solo ilustra la contención de rutas, no es código de JFrog ni una reproducción. Versiones afectadas: anteriores a 7.146.35 y de 7.161.0 a 7.161.16; corregidas: 7.146.35 y 7.161.16. JFrog afirma que los entornos Cloud afectados ya están protegidos. JFrog asigna severidad Medium; no se afirma una puntuación CVSS numérica.

Flujo de Ataque Paso a Paso

Paso 1

La solicitud autenticada llega a la caché remota

El problema requiere un usuario autenticado y las condiciones específicas indicadas por JFrog.

Paso 2

Falla el límite de la ruta de caché

El informe indica que Artifactory interpretó la solicitud manipulada de forma distinta al elegir la caché local y resolver el registro upstream: obtuvo un manifiesto y capas de un repositorio controlado por el agente, pero los guardó con el nombre de una imagen CyberGym confiable. El informe no publica la solicitud.

Paso 3

Impacto limitado a las pruebas

Una petición posterior de la imagen confiable podría haber recibido el contenido sustituido. OpenAI confirmó el envenenamiento de la caché, pero no encontró pruebas de que otra carga descargara o ejecutara esa imagen. En ese entorno de evaluación, la ejecución habría dependido de que un contenedor objetivo resolviera después la referencia confiable.

Código Fuente: Vulnerable vs. Seguro

Interfaces ilustrativas en Python; no son código de JFrog. El patrón vulnerable permite que analizadores separados obtengan la clave de caché y el origen upstream de una misma solicitud sin procesar. El patrón protegido resuelve un único RemoteImage inmutable y autorizado por el servidor, y usa ese mismo valor para la descarga y la identidad de caché. Cree ese objeto desde configuración validada, incluya el espacio de nombres remoto en la clave y vincule el contenido a un digest inmutable confiable cuando el sistema lo proporcione. Esto expresa el principio defensivo del informe, no es un analizador completo, un parche del producto ni una reproducción. Actualice a una versión corregida por JFrog.

IMPLEMENTACIÓN VULNERABLE
from typing import Callable

Fetch = Callable[[str], bytes]
Select = Callable[[str], str]

def cache_request(
    request: str,
    fetch: Fetch,
    select_local_key: Select,
    select_upstream: Select,
    cache: dict[str, bytes],
) -> None:
    local_key = select_local_key(request)
    upstream = select_upstream(request)
    cache[local_key] = fetch(upstream)
PARCHE SEGURO Y ROBUSTO
from dataclasses import dataclass
from typing import Protocol

@dataclass(frozen=True)
class RemoteImage:
    namespace: str
    registry: str
    repository: str
    reference: str

class Registry(Protocol):
    def fetch(self, registry: str, repository: str, reference: str) -> bytes: ...

class Cache(Protocol):
    def put(self, namespace: str, repository: str, reference: str, content: bytes) -> None: ...

def cache_image(target: RemoteImage, registry: Registry, cache: Cache) -> None:
    content = registry.fetch(target.registry, target.repository, target.reference)
    cache.put(target.namespace, target.repository, target.reference, content)

Lista de Verificación de Seguridad para Ingeniería

Fuentes