FlawOpen

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

● CVE-2026-63277 · CVSS v4 8.5 · Высокий
Исследования · FlawOpen

CVE-2026-63277: запуск Java при открытии таблицы Calc

Подготовленный документ Calc мог через внешнюю привязку данных заставить LibreOffice при открытии разрешить класс JDBC-драйвера из нелокального пути Java class path. В LibreOffice 26.2.5 и 26.8.0 такие записи допускаются только со схемой file; требуется, чтобы человек открыл документ, поэтому это не zero-click сетевая уязвимость.[1], [2], [3]

💡 Простыми словами (ELI5)

Таблица может хранить указание, где программе искать драйвер базы данных. До исправления это указание могло вести на удалённый адрес, откуда при открытии файла загружался Java-код. Исправление принимает только адреса со схемой file:. Это сужает путь, однако сама метка не доказывает, что каждая цель физически находится на локальном диске.[1], [4], [5]

Ключевые понятия и термины

Внешняя привязка данных
Параметр Calc, описывающий подключение листа к внешнему источнику данных.[1]
Драйвер JDBC
Программный компонент Java, который позволяет приложению обмениваться данными с базой.[1]
Class path
Набор мест, откуда Java может загружать классы и связанные ресурсы.[4], [5]
CWE-829
Включение функциональности из недоверенной области управления; в этом случае путь принимал URL class path вне ожидаемой схемы локального файла.[6], [1]

Связи со стандартами

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-829

Анализ первопричины

Уязвимый путь jvmaccess::ClassPath::translateToUrls раскрывал каждую запись Java class path и передавал её конструктору URL Java, предварительно не ограничив схему. В коде 26.2.5.2 добавлен разбор URI и отклонение записей со схемой, отличной от file, до создания URL Java. Уведомление поставщика связывает этот путь с внешними привязками данных Calc и загрузкой драйверов JDBC. Для достижения описанного поведения человек должен открыть подготовленный документ; процитированные источники не подтверждают zero-click или эксплуатацию в реальных атаках. Проверка касается именно схемы: не называйте её общей политикой авторизации и не утверждайте, что она отклоняет любую удалённую файловую шару.[1], [4], [5]

CVE-2026-63277: условия загрузки Java из Calc

ПроверкаЗатронутое условие или подтверждённое поведениеЗащитное действие
Данные из документаВнешняя привязка данных в документе может задать путь class path для JDBC-драйвера.Считайте документы и ссылки из недоверенных источников недоверенным вводом; установите исправленную версию.
Действие пользователяЧтобы достичь описанного поставщиком пути загрузки, человек должен открыть документ.Не называйте это zero-click; проверьте процессы получения и открытия документов.
Предел исправленияПроверка схемы сама по себе не доказывает, что каждый адрес file: ведёт на физически локальный диск или что сетевые ресурсы отклоняются.Описывайте изменение как проверку схемы и не приписывайте ему более широкую гарантию авторизации или расположения.

Пошаговый сценарий атаки

Шаг 1

В документе есть внешняя привязка данных

Затронутый документ описывает внешний источник и путь class path для класса Java-драйвера базы данных.[1], [4]

Шаг 2

Человек открывает документ

Требуется действие пользователя; открытие произвольной таблицы не равнозначно удалённой zero-click-атаке.[1], [2]

Шаг 3

Java class path преобразуется в URL

До исправления translateToUrls передавал раскрытую запись конструктору URL Java, не проверяя, что схема указывает на локальный файл.[4]

Шаг 4

Указанный драйвер может быть загружен

Уведомление поставщика описывает этот путь загрузки Java. Последствия зависят от документа, настроек и интеграции Java; нельзя делать вывод, что уязвим каждый документ или экземпляр Calc.[1], [2]

Шаг 5

Исправленная версия отклоняет другие схемы

LibreOffice 26.2.5.2 разбирает URI и вызывает исключение до создания URL, если схема не равна file. Установите исправленную сборку поставщика или дистрибутива.[5], [1]

Исходный код: Уязвимый vs Защищённый вариант

Эти точечные фрагменты взяты из upstream-реализации translateToUrls для указанных версий. В 26.2.4.2 запись раскрывается и сразу передаётся созданию URL Java. В 26.2.5.2 добавлен разбор URI, а схемы кроме file отклоняются до создания URL. Diff подтверждает проверку схемы; уведомление поставщика подтверждает путь через Calc/JDBC. Это анализ исходного кода, а не исполняемый proof of concept.[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);
}

Чек-лист по защите системы для инженеров

Источники