FlawOpen

flawopen.com/Teardowns/cve-2026-63277-libreoffice-calc-jdbc-classpath-rce

● CVE-2026-63277 · CVSS v4 8.5 · 높음
보안 연구 · FlawOpen

CVE-2026-63277: Calc 문서를 열 때 Java 코드 실행

조작된 Calc 문서는 외부 데이터 매핑을 이용해 사용자가 스프레드시트를 열 때 LibreOffice가 로컬이 아닌 Java class path에서 JDBC 드라이버 클래스를 찾도록 할 수 있었습니다. LibreOffice 26.2.5와 26.8.0은 해당 항목에 file scheme만 허용합니다. 사람이 문서를 열어야 하므로 zero-click 네트워크 취약점은 아닙니다.[1], [2], [3]

💡 알기 쉬운 설명 (ELI5)

스프레드시트는 데이터베이스 드라이버를 찾을 위치를 메모해 둘 수 있습니다. 수정 전에는 그 메모가 원격 주소를 가리킬 수 있어 파일을 열 때 Java 코드가 로드될 수 있었습니다. 수정은 file: scheme이 붙은 주소만 받아들입니다. 경로를 제한하지만 이 표시만으로 모든 대상이 물리적으로 로컬에 있다고 증명되지는 않습니다.[1], [4], [5]

핵심 개념 및 용어

외부 데이터 매핑
시트가 외부 데이터 원본에 연결되는 방식을 설명하는 Calc 설정입니다.[1]
JDBC 드라이버
애플리케이션이 데이터베이스와 통신하도록 돕는 Java 소프트웨어입니다.[1]
Class path
Java가 클래스와 관련 리소스를 불러올 수 있는 위치의 집합입니다.[4], [5]
CWE-829
신뢰할 수 없는 통제 영역의 기능을 포함하는 문제입니다. 이 경로는 의도한 로컬 파일 scheme 밖의 class-path URL을 허용했습니다.[6], [1]

프레임워크 매핑

  • 이 가이드에서 설명하는 약점의 CWE 참조: CWE-829

근본 원인 분석 (Root Cause)

취약한 jvmaccess::ClassPath::translateToUrls 경로는 각 Java class-path 항목을 확장해 Java URL 생성자에 전달하면서 scheme을 먼저 제한하지 않았습니다. 26.2.5.2 소스는 URI 파싱을 추가하고 Java URL을 만들기 전에 scheme이 file이 아닌 항목을 거부합니다. 공급업체 권고문은 이 경로를 Calc 외부 데이터 매핑 및 JDBC 드라이버 로딩과 연결합니다. 조작된 문서를 사람이 열어야 하며, 인용된 자료는 zero-click 도달성이나 실제 악용을 입증하지 않습니다. 보호 검사는 scheme에 한정됩니다. 이를 일반 권한 정책이라고 하거나 모든 원격 파일 공유를 거부한다고 설명하지 마세요.[1], [4], [5]

CVE-2026-63277: Calc에서 Java 로딩으로 이어지는 조건

확인영향 조건 또는 확인된 동작방어 조치
문서가 제어하는 입력문서의 외부 데이터 매핑이 JDBC 드라이버에 쓰이는 class path를 지정할 수 있습니다.신뢰할 수 없는 문서와 링크를 비신뢰 입력으로 취급하고 수정된 버전을 설치하세요.
사용자 동작공급업체가 설명한 로딩 경로에 도달하려면 사람이 문서를 열어야 합니다.zero-click이라고 부르지 말고 문서 수신 및 열기 절차를 점검하세요.
수정 범위scheme 검사만으로 모든 file: 대상이 실제 로컬 디스크에 있거나 네트워크 공유가 거부된다고 증명할 수 없습니다.변경을 scheme 검증으로 설명하고 권한 또는 위치에 대한 더 넓은 보장을 주장하지 마세요.

단계별 공격 실행 흐름

단계 1

문서에 외부 데이터 매핑이 포함됨

영향받는 문서는 외부 원본과 Java 데이터베이스 드라이버 클래스의 class path를 설명합니다.[1], [4]

단계 2

사람이 문서를 엶

사용자 동작이 필요합니다. 임의의 스프레드시트를 여는 일을 원격 zero-click 공격과 같다고 볼 수 없습니다.[1], [2]

단계 3

Java class path가 URL로 변환됨

수정 전 translateToUrls는 확장된 항목을 Java URL 생성자에 넘기면서 scheme이 로컬 파일인지 확인하지 않았습니다.[4]

단계 4

참조된 드라이버가 로드될 수 있음

공급업체 권고문이 이 Java 로딩 경로를 설명합니다. 영향은 문서, 구성, Java 통합에 달려 있으므로 모든 Calc 문서나 설치가 취약하다고 단정하면 안 됩니다.[1], [2]

단계 5

수정 버전이 다른 scheme을 거부함

LibreOffice 26.2.5.2는 URI를 파싱하고 scheme이 file이 아니면 URL 생성 전에 예외를 발생시킵니다. 공급업체 또는 배포판의 수정 빌드를 설치하세요.[5], [1]

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

이 발췌는 버전이 고정된 upstream translateToUrls 구현에서 가져왔습니다. 26.2.4.2는 항목을 확장한 뒤 바로 Java URL을 생성합니다. 26.2.5.2는 URI 파싱을 추가하고 URL 생성 전에 file 이외의 scheme을 거부합니다. diff는 scheme 검사를 입증하고 공급업체 권고문은 Calc/JDBC 문서 경로를 설명합니다. 이는 소스 분석이며 실행 가능한 PoC가 아닙니다.[4], [5], [1]

소스 코드 발췌: jvmaccess/source/classpath.cxx — translateToUrls 함수

취약한 구현
// LibreOffice 26.2.4.2 — jvmaccess/source/classpath.cxx (취약 버전)
if (!url.isEmpty()) {
    css::uno::Reference< css::uri::XVndSunStarExpandUrlReference >
        expUrl(
            css::uri::UriReferenceFactory::create(context)->parse(url),
            css::uno::UNO_QUERY);
    if (expUrl.is()) {
        css::uno::Reference< css::util::XMacroExpander > expander =
            css::util::theMacroExpander::get(context);
        try {
            url = expUrl->expand( expander );
        } catch (const css::lang::IllegalArgumentException & e) {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw css::lang::WrappedTargetRuntimeException(
                "com.sun.star.lang.IllegalArgumentException: "
                 + e.Message,
                 nullptr, anyEx );
        }
    }
    jvalue arg;
    arg.l = environment->NewString(
        reinterpret_cast< jchar const * >(url.getStr()),
        static_cast< jsize >(url.getLength()));
    if (arg.l == nullptr) {
        return nullptr;
    }
    jobject o(environment->NewObjectA(classUrl, ctorUrl, &arg));
    if (o == nullptr) {
        return nullptr;
    }
    urls.push_back(o);
}
보안 강화 패치
// LibreOffice 26.2.5.2 — jvmaccess/source/classpath.cxx (수정 버전)
if (!url.isEmpty()) {
    css::uno::Reference< css::uri::XVndSunStarExpandUrlReference >
        expUrl(
            css::uri::UriReferenceFactory::create(context)->parse(url),
            css::uno::UNO_QUERY);
    if (expUrl.is()) {
        css::uno::Reference< css::util::XMacroExpander > expander =
            css::util::theMacroExpander::get(context);
        try {
            url = expUrl->expand( expander );
        } catch (const css::lang::IllegalArgumentException & e) {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw css::lang::WrappedTargetRuntimeException(
                "com.sun.star.lang.IllegalArgumentException: "
                 + e.Message,
                 nullptr, anyEx );
        }
    }
    css::uno::Reference< css::uri::XUriReference > uriRef(
        css::uri::UriReferenceFactory::create(context)->parse(url));
    if (!uriRef.is() || !uriRef->getScheme().equalsIgnoreAsciiCase("file"))
    {
        throw css::lang::IllegalArgumentException(
            "non-local Java class path entry: " + url,
            css::uno::Reference< css::uno::XInterface >(), 0);
    }
    jvalue arg;
    arg.l = environment->NewString(
        reinterpret_cast< jchar const * >(url.getStr()),
        static_cast< jsize >(url.getLength()));
    if (arg.l == nullptr) {
        return nullptr;
    }
    jobject o(environment->NewObjectA(classUrl, ctorUrl, &arg));
    if (o == nullptr) {
        return nullptr;
    }
    urls.push_back(o);
}

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

출처