FlawOpen

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

● CVE-2026-21589 · CVSS v4 9.3 · 심각
보안 연구 · FlawOpen

CVE-2026-21589: Atlassian Data Center 파일 접근 취약점

Atlassian 권고문과 10월 12일 MS-ISAC 보고는 위험과 긴급성을 보여 주지만, 특정 고객 인스턴스의 악용을 입증하지는 않습니다.[1], [4], [5], [10]

💡 알기 쉬운 설명 (ELI5)

방문객이 말한 서가 위치를 받아들이는 도서관을 떠올려 보세요. 모든 서가를 둘러볼 수는 없지만 특정 책의 위치를 이미 알고 있다면, 경계 검사 오류로 직원이 공개 열람 구역 밖에서 책을 가져올 수 있습니다. 수정은 해석된 경로를 허용된 범위 안에 두고 제품별 업데이트를 적용하는 것입니다.[1], [4]

핵심 개념 및 용어

임의 파일 접근
요청이 의도된 범위 밖의 파일에 접근할 수 있는 상태입니다. Atlassian은 전체 파일 시스템이 아니라 웹 애플리케이션 루트 아래 특정 파일에 대한 접근을 설명합니다.[1], [4]
경로 순회
경로 구성 요소가 의도한 디렉터리 경계를 넘어섭니다. Atlassian의 완화책은 URL의 순회 표식과 인코딩된 변형을 대상으로 합니다.[1], [4]
Data Center
Atlassian의 자체 관리형 배포 모델입니다. 공지는 제품 8종을 나열하며 Atlassian은 Cloud 제품은 패치되었다고 밝혔습니다.[1]
경로 사전 지식
대상 파일의 정확한 이름과 경로를 미리 알아야 합니다. 공지에 따르면 이 취약점으로 디렉터리를 열거할 수는 없습니다.[1]
CVSS 4.0
Atlassian은 자체 평가에서 이 취약점을 Critical, CVSS 4.0 9.3으로 분류했습니다.[1]
Router.unescapeSlashes()
공유 라이브러리는 리소스 처리 전에 인코딩된 구분자 ::를 /로 되돌립니다. watchTowr의 소스 분석에서 이 디코딩 경계가 핵심입니다.[1], [4]
ResourceServingHelpers
이 헬퍼는 리소스 이름을 해석하고 남은 상대 경로를 createResourceWithRelativePath()에 전달합니다.[1], [4]
Crowd 자격 증명 체인
구성에 따라 이어질 수 있는 단계입니다. 읽을 수 있는 Crowd 속성 파일이 애플리케이션 암호를 노출할 수 있으며, 사용 가능성은 네트워크 접근과 권한에 달립니다.[4], [5]
CWE-22
경로 탐색 약점에 대한 FlawOpen의 편집상 분류입니다. 경로가 의도한 디렉터리 안으로 충분히 제한되지 않았습니다. Atlassian 공지에는 CWE가 지정되지 않았습니다.[6]

프레임워크 매핑

  • MITRE CWE에 대한 FlawOpen의 개념적 매핑입니다. 공급업체나 CVE 레코드가 이 연구 시연에 해당 약점 ID를 지정한 것은 아닙니다. CWE-22

근본 원인 분석 (Root Cause)

watchTowr의 공유 라이브러리 atlassian-plugins-webresource 분석은 경로 디코딩 경계를 짚습니다. Router.unescapeSlashes()는 ::를 /로 바꾸며, 리소스 경로는 핸들러를 호출하기 전에 요청자가 제어하는 경로 구성 요소에 이 함수를 적용합니다. 후속 조회는 리소스 이름을 분리해 남은 상대 경로를 createResourceWithRelativePath()에 전달합니다. 이때 대체 구분자 표현이 경로 순회 검사 가정을 우회할 수 있습니다. 연구진은 Jira, Confluence, Bitbucket에서 Tomcat 애플리케이션 컨텍스트 안의 파일 읽기를 시연했고 Atlassian은 8개 제품을 영향 대상으로 명시했습니다. 보고서에 따르면 테스트한 경로는 Tomcat 컨텍스트 밖으로 나가지 않았습니다. 조건이 맞으면 읽을 수 있는 WEB-INF/classes/crowd.properties에서 Crowd 애플리케이션 암호가 평문으로 노출될 수 있으며, 실제 영향은 구성과 Crowd 네트워크 접근성에 달립니다. BleepingComputer는 기술 보고서와 PoC 공개 몇 시간 뒤 Previdian 센서가 시도를 관찰했다고 보도했습니다. 이는 허니팟 대상 시도이며 고객 시스템 침해 성공의 증거는 아닙니다. 아래 취약 Java 발췌는 watchTowr 보고서에 공개된 내용입니다. 안전한 코드는 일반적인 경로 제한 패턴이며 공개되지 않은 Atlassian 패치 코드가 아닙니다. 제품별 공식 업데이트를 적용하세요.[1], [4], [5]

단계별 공격 실행 흐름

단계 1

공유 디코더가 경로를 변환

Atlassian 웹 리소스 라우터는 리소스를 제공하기 전에 대체 구분자 ::를 /로 바꿉니다. 정상적인 리소스 라우팅 과정에서 발생합니다.[1], [4]

단계 2

디코딩된 경로가 리소스 조회로 전달

헬퍼가 경로 구성 요소를 나누고 남은 상대 경로를 리소스 팩토리에 전달합니다. 대체 구분자는 순회 검사 가정을 우회할 수 있습니다.[4]

단계 3

애플리케이션 컨텍스트 파일을 읽을 수 있음

watchTowr는 Jira, Confluence, Bitbucket에서 이를 시연했습니다. 테스트 경로는 Tomcat 컨텍스트를 벗어나지 않았다고 보고됐으며 임의 호스트 파일 접근으로 확대 해석하면 안 됩니다.[1], [4]

단계 4

ID 관리 체인은 구성에 따라 달라짐

읽을 수 있는 Crowd 설정에 애플리케이션 암호가 있고 Crowd가 네트워크로 접근 가능하며 해당 자격 증명을 허용하면 ID 기능에 접근될 수 있습니다.[4], [5]

단계 5

관찰된 시도는 긴급성을 높이지만 침해를 증명하지 않음

10월 12일 Center for Internet Security의 MS-ISAC 권고문은 CVE-2026-21589가 실제 환경에서 악용되고 있다는 보고가 있다고 밝혔습니다. 피해자를 특정하거나 사건 증거를 공개하지 않았으므로, 이를 MS-ISAC가 전한 악용 보고로 표현해야 하며 특정 고객에 대한 접근이 확인된 것으로 말해서는 안 됩니다. Atlassian의 10월 5일 권고문은 Cloud 제품에서 악용 증거를 찾지 못했다고 밝혔으며, 해당 제품에는 패치가 적용되었다고 했습니다. 이 조사 결과는 Cloud에 한정되므로 취약점 악용 보고를 전한 MS-ISAC의 발표와 모순되지 않습니다. 어느 발표도 특정 인스턴스에서 발생한 일을 확정하지 않습니다.[1], [10]

소스 코드 비교: 취약한 구현 vs 보안 패치

취약 코드 발췌는 watchTowr가 공개한 영향을 받는 web-resource 라이브러리의 소스 경로 변환을 보여주며, 익스플로잇 요청이 아닙니다. 아래 예시는 일반적인 Java 방어 방법이며 Atlassian 패치가 아닙니다. 신뢰할 수 있는 루트에서 SecureDirectoryStream 핸들을 사용해 경로를 단계별로 탐색하고, 각 구성 요소의 심볼릭 링크를 거부하며, 읽는 바이트 수를 제한합니다. 호출자는 한 번만 디코딩된 상대 경로와 신뢰할 수 있는 디렉터리를 루트로 연 SecureDirectoryStream을 전달해야 합니다. 일부 파일 시스템 공급자는 SecureDirectoryStream을 지원하지 않으므로, 이 경우 안전하게 실패해야 합니다. 제품별 공급자 수정 버전을 설치하세요.이 예시는 신뢰할 수 있는 리소스 트리를 전제로 합니다. 신뢰할 수 없는 주체가 디렉터리와 파일을 변경할 수 없어야 하며 일반 파일만 포함해야 합니다. 코드 자체가 파일 유형을 검증하지는 않습니다. 바이트 제한과 진행이 없는 읽기에 대한 제한된 처리는 읽기량을 제한하지만, 범용 파일 제공 루틴은 아닙니다. 호출자는 한 번만 디코딩된 상대 경로를 전달하고 신뢰할 수 있는 루트 스트림을 열린 상태로 유지해야 합니다. 파일 시스템 공급자가 SecureDirectoryStream을 지원하지 않을 수 있으므로 사용할 수 없으면 안전하게 실패하세요.[4], [7]

취약한 구현
public static String unescapeSlashes(String string) {
    return string.replaceAll("::", "/");
}
보안 강화 패치
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();
    }
}

엔지니어링 및 시스템 보안 강화 체크리스트

출처