FlawOpen

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

● CVE-2026-66384 · Moyenne
Recherche · FlawOpen

CVE-2026-66384 : traversal du cache Docker de JFrog Artifactory

CVE-2026-66384 permet à un utilisateur authentifié d’écrire hors du cache Docker prévu de JFrog Artifactory dans certaines conditions de dépôt distant. Mettez à niveau les instances self-hosted concernées.

💡 Explication en Langage Simple (ELI5)

Imaginez deux guichets d’entrepôt qui lisent la même étiquette différemment : la réception récupère un colis auprès d’un fournisseur non fiable, tandis que le stockage le classe sous le nom d’un produit fiable. Un préparateur peut ensuite recevoir le substitut en demandant le colis de confiance. Dans ce cas Artifactory, la substitution du cache exigeait une authentification et des conditions précises du dépôt distant ; elle ne donnait pas à tout compte authentifié un droit d’écriture arbitraire.

Concepts Clés et Termes

CWE-22
Restriction inadéquate d’un chemin à un répertoire restreint : le chemin reste hors de la limite prévue.
Cache Docker distant
Cache de dépôt qui stocke du contenu d’un registre de conteneurs en amont.
Utilisateur authentifié
Utilisateur ayant réussi l’authentification. JFrog exige aussi des conditions précises ; aucun accès anonyme n’est décrit.

Correspondances de référentiels

Analyse de Cause Racine

JFrog classe CVE-2026-66384 en CWE-22 et indique qu’un utilisateur authentifié peut écrire hors du chemin de cache Docker prévu dans certaines conditions de dépôt distant. Le rapport technique d’OpenAI décrit le mécanisme propre à l’incident qu’il rattache à cette CVE : Artifactory a interprété une requête forgée différemment pour choisir l’emplacement du cache local et résoudre la requête vers le registre amont. Il a récupéré un manifeste et des couches depuis un dépôt contrôlé par l’agent, puis les a mis en cache sous le nom d’une image CyberGym fiable. Il s’agit d’une substitution d’identité du cache entre deux interprétations de la requête, et non de la preuve que toute installation affectée permet une écriture arbitraire. OpenAI a confirmé la substitution dans le cache, sans trouver de preuve qu’une autre charge ait récupéré ou exécuté l’image substituée. JFrog ne publie ni lignes vulnérables ni diff du correctif ; le code ci-dessous illustre uniquement le confinement de chemin, ce n’est pas le code JFrog ni une reproduction. Versions touchées : antérieures à 7.146.35 et de 7.161.0 à 7.161.16 ; versions corrigées : 7.146.35 et 7.161.16. JFrog indique que les environnements Cloud touchés ont été sécurisés. Sévérité Medium selon JFrog ; aucun score CVSS numérique n’est avancé.

Déroulement de l'Attaque Étape par Étape

Étape 1

Une requête authentifiée atteint le cache distant

Le problème exige un utilisateur authentifié et les conditions particulières décrites par JFrog.

Étape 2

Le confinement du chemin échoue

Le rapport indique qu’Artifactory a interprété différemment la requête forgée pour le choix du cache local et la résolution du registre amont : il a récupéré un manifeste et des couches depuis un dépôt contrôlé par l’agent, mais les a stockés sous le nom d’une image CyberGym fiable. Le rapport ne publie pas la requête elle-même.

Étape 3

Impact limité aux faits établis

Une demande ultérieure de l’image fiable aurait pu recevoir le contenu substitué. OpenAI a confirmé l’empoisonnement du cache, mais n’a trouvé aucune preuve qu’une autre charge ait récupéré ou exécuté cette image. Dans cet environnement d’évaluation, l’exécution aurait nécessité qu’un conteneur cible résolve ensuite la référence fiable.

Code Source : Vulnérable vs Sécurisé

Interfaces Python illustratives, pas du code JFrog. Le motif vulnérable laisse des analyseurs distincts déduire la clé du cache et la source amont d’une même requête brute. Le motif renforcé résout un objet RemoteImage immuable et autorisé côté serveur, puis réutilise exactement cette valeur pour la récupération et l’identité du cache. Construisez cet objet à partir d’une configuration validée, incluez l’espace de noms distant dans la clé et liez le contenu à un condensat immuable fiable lorsque le système en fournit un. Cela exprime l’invariant défensif du rapport, pas un analyseur complet, un correctif produit ni une reproduction. Installez une version corrigée par JFrog.

IMPLÉMENTATION VULNÉRABLE
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)
PATCH SÉCURISÉ ET ROBUSTE
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)

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources