flawopen.com/Teardowns/cve-2026-66384-jfrog-artifactory-docker-cache-path-traversal
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.
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
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-22
- CISA KEV — ajouté le 2026-08-27 — CVE-2026-66384
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
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.
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.
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.
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)
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
- ✓Inventoriez Artifactory self-hosted et mettez à niveau les branches concernées vers 7.146.35 ou 7.161.16 ; confirmez la cible auprès de JFrog.
- ✓Examinez les dépôts Docker distants et limitez les droits d’écriture du cache aux identités nécessaires.
- ✓Après la mise à niveau, examinez les journaux d’audit pour repérer les écritures ou changements d’image inattendus ; enquêtez, car la mise à niveau n’annule pas les changements antérieurs.