FlawOpen

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

● CVE-2026-21589 · CVSS v4 9.3 · Crítica
Pesquisa · FlawOpen

CVE-2026-21589: acesso a arquivos no Atlassian Data Center

O aviso da Atlassian e o relato do MS-ISAC de 12 de outubro confirmam o risco e a urgência, mas não comprovam exploração em uma instalação específica de cliente.[1], [4], [5], [10]

💡 Explicação em Linguagem Simples (ELI5)

Imagine uma biblioteca que aceita do visitante o endereço de uma estante. Ele não pode percorrer todas as estantes, mas, se já souber onde está um livro específico, uma checagem de limite defeituosa pode fazer o atendente buscá-lo fora da área pública. A correção mantém cada caminho resolvido dentro da área permitida e instala a atualização própria do produto.[1], [4]

Conceitos Centrais e Termos

Acesso arbitrário a arquivos
Uma requisição acessa arquivos fora do escopo previsto. A Atlassian descreve acesso a arquivos específicos na raiz web, não ao sistema de arquivos inteiro.[1], [4]
Travessia de caminho
Componentes de caminho cruzam o limite do diretório esperado. A mitigação da Atlassian trata marcadores de travessia e formas codificadas na URL.[1], [4]
Data Center
Modelo de implantação autogerenciado da Atlassian. O aviso lista oito produtos afetados; a empresa diz que seus produtos Cloud já foram corrigidos.[1]
Caminho previamente conhecido
É preciso saber antecipadamente o nome e o caminho exatos do arquivo; o aviso afirma que a falha não enumera diretórios.[1]
CVSS 4.0
A Atlassian classifica a falha como Crítica, CVSS 4.0 9,3, segundo sua própria avaliação.[1]
Router.unescapeSlashes()
A biblioteca compartilhada converte o separador codificado :: de volta para / antes de tratar o recurso; esse limite de decodificação é central à análise da watchTowr.[1], [4]
ResourceServingHelpers
O helper resolve o nome do recurso e envia o caminho relativo restante a createResourceWithRelativePath().[1], [4]
Cadeia de credenciais do Crowd
Possível etapa posterior dependente da configuração: um arquivo de propriedades Crowd legível pode revelar uma senha de aplicação; rede e autorização determinam se ela pode ser usada.[4], [5]
CWE-22
Classificação editorial da FlawOpen para a falha de traversal de caminho: o caminho não fica suficientemente restrito ao diretório previsto. O advisory da Atlassian não atribui um CWE.[6]

Mapeamentos de frameworks

  • Mapeamento conceitual da FlawOpen para o MITRE CWE; nenhum fornecedor ou registro CVE atribuiu este identificador de fraqueza a esta demonstração de pesquisa. CWE-22

Análise de Causa Raiz

A análise da watchTowr da biblioteca compartilhada atlassian-plugins-webresource identifica o limite de decodificação do caminho. Router.unescapeSlashes() converte :: em /; as rotas de recursos aplicam essa função a componentes controlados pela requisição antes de chamar os handlers. A busca posterior divide o nome do recurso e envia o caminho restante a createResourceWithRelativePath(), onde a representação alternativa do separador pode contornar a verificação de traversal. Pesquisadores demonstraram leitura de arquivos dentro do contexto da aplicação Tomcat em Jira, Confluence e Bitbucket; a Atlassian lista oito produtos afetados. O relatório informa que os testes não saíram do contexto Tomcat. Em uma cadeia condicional, um WEB-INF/classes/crowd.properties legível pode expor a senha de aplicação Crowd em texto claro; o impacto depende da configuração e da acessibilidade do Crowd. O BleepingComputer relata que sensores da Previdian observaram tentativas poucas horas após a publicação da análise técnica e do PoC. Isso comprova tentativas em honeypots, não acesso bem-sucedido a sistemas de clientes. Os trechos Java vulneráveis abaixo foram publicados pela watchTowr. A defesa inferior é um padrão geral, não o patch Atlassian, que não foi publicado como código-fonte completo; instale a correção oficial de cada produto.[1], [4], [5]

Fluxo de Ataque Passo a Paso

Passo 1

O decodificador compartilhado transforma o caminho

O roteador de recursos web da Atlassian converte o separador alternativo :: em / antes de servir o recurso. Essa decodificação ocorre no fluxo normal de recursos.[1], [4]

Passo 2

A busca recebe o caminho decodificado

O helper percorre componentes do caminho e encaminha o restante ao resource factory. O separador alternativo pode contornar as suposições da verificação de traversal.[4]

Passo 3

Arquivos do contexto da aplicação ficam acessíveis

A watchTowr demonstrou o comportamento em Jira, Confluence e Bitbucket. O relatório diz que os testes não saíram do contexto Tomcat; não generalize para arquivos arbitrários do host.[1], [4]

Passo 4

Uma cadeia de identidade depende da configuração

Se a configuração Crowd legível contiver a senha da aplicação e o Crowd aceitar essa credencial pela rede, ela poderá permitir acesso a funções de identidade. Listas de permissão e configuração mudam o resultado.[4], [5]

Passo 5

Tentativas observadas aumentam a urgência, mas não provam invasão

Em 12 de outubro, o aviso do MS-ISAC do Center for Internet Security informou que havia relatos de exploração do CVE-2026-21589 em ambiente real. O aviso não identificou vítimas nem publicou evidências do incidente; portanto, trate isso como um sinal de exploração relatado pelo MS-ISAC, não como acesso confirmado a um cliente identificado. O aviso da Atlassian de 5 de outubro disse que sua investigação não havia encontrado evidências de exploração nos produtos Cloud, que já haviam sido corrigidos. Essa conclusão se limitava ao Cloud e não contradiz o relato do MS-ISAC sobre exploração da vulnerabilidade; nenhuma das declarações determina o que ocorreu em uma instalação específica.[1], [10]

Código-Fonte: Vulnerável vs. Seguro

O trecho vulnerável mostra a transformação de caminho no código-fonte publicada pela watchTowr para a biblioteca web-resource afetada; não é uma requisição de exploração. O exemplo abaixo é uma defesa geral em Java, não o patch da Atlassian: percorre o caminho a partir de uma raiz confiável usando handles SecureDirectoryStream, rejeita links simbólicos em cada componente e limita os bytes lidos. O chamador deve fornecer um caminho relativo decodificado uma única vez e um SecureDirectoryStream enraizado em um diretório confiável. Provedores de sistema de arquivos podem não oferecer suporte a SecureDirectoryStream; nesse caso, falhe de forma segura. Instale a correção específica do produto fornecida pelo fabricante. Este exemplo pressupõe uma árvore de recursos confiável, cujos diretórios e arquivos não possam ser alterados por usuários não confiáveis e que contenha apenas arquivos regulares; ele não verifica o tipo de arquivo por conta própria. O limite de bytes e o tratamento limitado de leituras sem progresso restringem a leitura, mas isso não é uma rotina universal para servir arquivos. O chamador deve fornecer o caminho relativo decodificado uma única vez e manter um fluxo raiz confiável. O suporte do provedor do sistema de arquivos é opcional; falhe de forma segura se SecureDirectoryStream não estiver disponível.[4], [7]

IMPLEMENTAÇÃO VULNERÁVEL
public static String unescapeSlashes(String string) {
    return string.replaceAll("::", "/");
}
PATCH SEGURO E ROBUSTO
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();
    }
}

Lista de Verificação de Segurança para Engenharia

Fontes