flawopen.com/Teardowns/cve-2026-63277-libreoffice-calc-jdbc-classpath-rce
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]
스프레드시트는 데이터베이스 드라이버를 찾을 위치를 메모해 둘 수 있습니다. 수정 전에는 그 메모가 원격 주소를 가리킬 수 있어 파일을 열 때 Java 코드가 로드될 수 있었습니다. 수정은 file: scheme이 붙은 주소만 받아들입니다. 경로를 제한하지만 이 표시만으로 모든 대상이 물리적으로 로컬에 있다고 증명되지는 않습니다.[1], [4], [5]
핵심 개념 및 용어
외부 데이터 매핑- 시트가 외부 데이터 원본에 연결되는 방식을 설명하는 Calc 설정입니다.[1]
JDBC 드라이버- 애플리케이션이 데이터베이스와 통신하도록 돕는 Java 소프트웨어입니다.[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 검증으로 설명하고 권한 또는 위치에 대한 더 넓은 보장을 주장하지 마세요. |
단계별 공격 실행 흐름
Java class path가 URL로 변환됨
수정 전 translateToUrls는 확장된 항목을 Java URL 생성자에 넘기면서 scheme이 로컬 파일인지 확인하지 않았습니다.[4]
소스 코드 비교: 취약한 구현 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);
}
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓26.2 계열은 LibreOffice 26.2.5 이상, 26.8 계열은 26.8.0 이상으로 업데이트하고 배포판 패키지 및 backport 여부를 확인하세요.[1], [2]
- ✓신뢰할 수 없는 출처의 Calc 문서를 여는 업무 흐름과 JDBC 드라이버에 의존하는 외부 데이터 매핑을 조사하세요.
- ✓Java URL 생성 전에
file이외 class-path scheme을 거부하고 지원되는 로컬 파일 항목은 계속 동작하는지 회귀 테스트로 검증하세요.[5] - ✓매크로 경고를 이 Java class-path 경로의 방어로 간주하지 말고 문서 열기 동작과 Java 설정을 각각 점검하세요.[1]
- ✓Apache 권고문에 따르면 LibreOffice는 같은 문제를 CVE-2026-63277로 보고했고, Apache OpenOffice는 CVE-2026-59265로 추적합니다. AOO 자체의 영향 버전과 수정 버전 및 완화책을 확인하고 LibreOffice 버전 안내를 재사용하지 마세요.[8], [1]
- ✓테스트에서 유포된 문서나 원격 Java 페이로드를 재현하지 마세요. scheme 검증을 확인하는 통제된 비실행 단언을 사용하세요.[1]