FlawOpen

flawopen.com/Teardowns/cve-2026-21589-atlassian-arbitrary-file-access

● CVE-2026-21589 · CVSS v4 9.3 · Critique
Recherche · FlawOpen

CVE-2026-21589 : accès aux fichiers Atlassian Data Center

L’avis d’Atlassian et le signalement du MS-ISAC du 12 octobre établissent le risque et l’urgence, mais ne prouvent pas une exploitation chez un client précis.[1], [4], [5], [10]

💡 Explication en Langage Simple (ELI5)

Imaginez une bibliothèque qui accepte l’emplacement d’une étagère indiqué par un visiteur. Il ne peut pas parcourir toutes les étagères, mais s’il connaît déjà l’emplacement d’un livre précis, une vérification de frontière défectueuse peut amener l’employé à le chercher hors de la zone publique. Le correctif maintient chaque chemin dans la zone autorisée et met à jour le produit concerné.[1], [4]

Concepts Clés et Termes

Accès arbitraire aux fichiers
Une requête accède à des fichiers hors du périmètre prévu. Atlassian décrit l’accès à des fichiers précis sous la racine web, pas à tout le système de fichiers.[1], [4]
Traversée de chemin
Des composants de chemin franchissent la limite du répertoire prévu. La mesure Atlassian vise les marqueurs de traversée et leurs variantes encodées dans les URL.[1], [4]
Data Center
Modèle d’hébergement autogéré d’Atlassian. L’avis cite huit produits concernés ; Atlassian indique que ses produits Cloud sont corrigés.[1]
Chemin connu
Le nom et le chemin exacts du fichier doivent être connus à l’avance ; l’avis précise que la faille ne permet pas d’énumérer les répertoires.[1]
CVSS 4.0
Atlassian classe la faille Critique, CVSS 4.0 à 9,3, selon sa propre évaluation.[1]
Router.unescapeSlashes()
La bibliothèque partagée reconvertit le séparateur encodé :: en / avant le traitement de la ressource ; cette frontière de décodage est centrale dans l’analyse de watchTowr.[1], [4]
ResourceServingHelpers
Le helper résout le nom de ressource et transmet le chemin relatif restant à createResourceWithRelativePath().[1], [4]
Chaîne d’identifiants Crowd
Étape ultérieure conditionnelle : un fichier de propriétés Crowd lisible peut révéler un mot de passe applicatif ; la joignabilité réseau et les droits déterminent son utilité.[4], [5]
CWE-22
Classement éditorial de FlawOpen pour la faille de traversée de chemin : le chemin n’est pas suffisamment limité au répertoire prévu. L’avis d’Atlassian n’attribue aucun CWE.[6]

Correspondances de référentiels

  • Correspondance conceptuelle de FlawOpen avec MITRE CWE ; aucun fournisseur ni enregistrement CVE n’a attribué cet identifiant de faiblesse à cette démonstration de recherche. CWE-22

Analyse de Cause Racine

L’analyse par watchTowr de la bibliothèque partagée atlassian-plugins-webresource identifie le point de décodage du chemin. Router.unescapeSlashes() transforme :: en / ; les routes de ressources appliquent cette fonction à des composants contrôlés par la requête avant d’appeler les gestionnaires de ressources. La recherche en aval découpe le nom de ressource et transmet le chemin restant à createResourceWithRelativePath(), où la représentation alternative du séparateur peut contourner la vérification de traversal. Les chercheurs ont démontré la lecture de fichiers dans le contexte applicatif Tomcat sur Jira, Confluence et Bitbucket ; Atlassian cite huit produits touchés. Leur rapport précise que le traversal testé ne sortait pas du contexte Tomcat. Dans une chaîne conditionnelle, un WEB-INF/classes/crowd.properties lisible peut révéler le mot de passe applicatif Crowd en clair ; l’impact dépend de la configuration et de l’accessibilité de Crowd. BleepingComputer rapporte que des capteurs Previdian ont observé des tentatives quelques heures après la publication de l’analyse et du PoC. Il s’agit de tentatives sur des honeypots, pas d’une preuve d’accès réussi chez un client. Les extraits Java vulnérables ci-dessous sont publiés par watchTowr. La défense proposée est un motif général, pas le correctif Atlassian dont le code complet n’a pas été publié ; installez le correctif officiel de chaque produit.[1], [4], [5]

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

Étape 1

Le décodeur partagé transforme le chemin

Le routeur de ressources web Atlassian remplace le séparateur alternatif :: par / avant de servir une ressource. Ce décodage appartient au routage normal des ressources.[1], [4]

Étape 2

La recherche reçoit le chemin décodé

Le gestionnaire parcourt les composants et transmet le chemin relatif restant à la fabrique de ressources. Le séparateur alternatif peut déjouer les hypothèses du contrôle de traversal.[4]

Étape 3

Des fichiers du contexte applicatif deviennent lisibles

watchTowr l’a démontré sur Jira, Confluence et Bitbucket. Le rapport indique que les essais ne sortaient pas du contexte Tomcat ; ne généralisez pas à tous les fichiers de l’hôte.[1], [4]

Étape 4

La chaîne d’identité dépend de la configuration

Si la configuration Crowd lisible contient son mot de passe applicatif et que Crowd est accessible et accepte cet identifiant, des fonctions d’identité peuvent devenir accessibles. Les listes d’adresses et le déploiement comptent.[4], [5]

Étape 5

Les tentatives observées renforcent l’urgence, sans prouver une compromission

Le 12 octobre, l’avis MS-ISAC du Center for Internet Security indiquait que des signalements faisaient état d’une exploitation de CVE-2026-21589 dans la nature. Il ne nommait aucune victime et ne publiait aucun élément d’incident : il s’agit donc d’un signalement d’exploitation du MS-ISAC, et non d’un accès confirmé chez un client identifié. L’avis d’Atlassian du 5 octobre indiquait que son enquête n’avait trouvé aucun signe d’exploitation dans ses produits Cloud, qui avaient été corrigés. Cette conclusion concernait Cloud et ne contredit pas le signalement du MS-ISAC sur l’exploitation de la vulnérabilité ; aucune des déclarations ne permet de conclure sur une instance précise.[1], [10]

Code Source : Vulnérable vs Sécurisé

L’extrait vulnérable montre la transformation du chemin dans le code source publiée par watchTowr pour la bibliothèque web-resource concernée ; il ne s’agit pas d’une requête d’exploitation. L’exemple inférieur présente une défense Java générale, pas le correctif d’Atlassian : il parcourt le chemin depuis une racine de confiance à l’aide de handles SecureDirectoryStream, rejette les liens symboliques à chaque composant et limite le nombre d’octets lus. L’appelant doit fournir un chemin relatif décodé une seule fois et un SecureDirectoryStream ancré dans un répertoire de confiance. Certains fournisseurs de systèmes de fichiers ne prennent pas en charge SecureDirectoryStream ; dans ce cas, échouez de manière sûre. Installez la mise à jour propre au produit publiée par le fournisseur. Cet exemple exige une arborescence de ressources de confiance, dont les répertoires et fichiers ne sont pas modifiables par des utilisateurs non fiables et qui ne contient que des fichiers ordinaires ; il ne vérifie pas lui-même le type de fichier. La limite d’octets et le traitement borné des lectures sans progression limitent la lecture, mais il ne s’agit pas d’une routine universelle de diffusion de fichiers. L’appelant doit fournir le chemin relatif décodé une seule fois et conserver un flux racine de confiance. La prise en charge par le fournisseur du système de fichiers est facultative ; échouez de manière sûre si SecureDirectoryStream est indisponible.[4], [7]

IMPLÉMENTATION VULNÉRABLE
public static String unescapeSlashes(String string) {
    return string.replaceAll("::", "/");
}
PATCH SÉCURISÉ ET ROBUSTE
static byte[] readUnder(SecureDirectoryStream<Path> root, Path relative, int maxBytes) throws IOException {
    if (relative.isAbsolute() || relative.getNameCount() == 0 || maxBytes < 0) {
        throw new SecurityException("Expected a relative path and non-negative limit");
    }
    List<SecureDirectoryStream<Path>> opened = new ArrayList<>();
    SecureDirectoryStream<Path> parent = root;
    try {
        for (int i = 0; i < relative.getNameCount() - 1; i++) {
            Path part = relative.getName(i);
            if (part.toString().isEmpty() || part.toString().equals(".") || part.toString().equals("..")) {
                throw new SecurityException("Invalid path component");
            }
            parent = parent.newDirectoryStream(part, LinkOption.NOFOLLOW_LINKS);
            opened.add(parent);
        }
        Path leaf = relative.getFileName();
        if (leaf.toString().isEmpty() || leaf.toString().equals(".") || leaf.toString().equals("..")) {
            throw new SecurityException("Invalid file name");
        }
        try (SeekableByteChannel channel = parent.newByteChannel(
                leaf, Set.of(StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS))) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteBuffer buffer = ByteBuffer.allocate(8192);
            int total = 0;
            int zeroReads = 0;
            while (true) {
                int count = channel.read(buffer);
                if (count == -1) break;
                if (count == 0) {
                    if (++zeroReads >= 3) throw new IOException("Resource read made no progress");
                    continue;
                }
                zeroReads = 0;
                if (count > maxBytes - total) throw new IOException("Resource exceeds size limit");
                out.write(buffer.array(), 0, count);
                total += count;
                buffer.clear();
            }
            return out.toByteArray();
        }
    } finally {
        for (int i = opened.size() - 1; i >= 0; i--) opened.get(i).close();
    }
}

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

Sources