
Prova de conceito de exploit para CVE-2026-22738, uma injeção crítica de SpEL com RCE no Spring AI SimpleVectorStore, demonstrando execução de comandos do sistema operacional sem autenticação.
Execução remota de código não autenticada via injeção de Spring Expression Language (SpEL) em SimpleVectorStore.similaritySearch().
| Artefato | Vulnerável | 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 |
Commit da correção: ba9220b22383e430d5f801ce8e4fa01cf9e75f29
SimpleVectorStore.similaritySearch() passa o nome da chave de filtro fornecida pelo chamador, sem validação, para um template SpEL avaliado por StandardEvaluationContext. Como StandardEvaluationContext expõe toda a API de reflexão da JVM, um atacante pode injetar:
T(java.lang.Runtime).getRuntime().exec(...)
na chave de filtro para obter execução de comandos do sistema operacional sem autenticação.
A injeção ingênua não funciona de imediato — duas peculiaridades do parser precisam ser contornadas:
' é tratada como string entre aspas; as aspas externas são removidas, corrompendo o payload."..." faz com que o parser remova as aspas duplas externas, deixando a expressão SpEL interna intacta como o valor da chave passado para #metadata['<KEY>'].#metadata[''] em ambos os lados da expressão injetada (em vez de uma variável indefinida) evita o erro SpEL unknown variable enquanto ainda dispara exec().A expressão avaliada resultante assume a forma:
#metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c','<cmd>'}) + #metadata[''] == 'x'
O corpo da resposta HTTP conterá EL1030E — o erro de runtime do SpEL:
operator ADD not supported between null and java.lang.ProcessImpl
Este erro é gerado após o retorno de exec(), confirmando a execução de comandos no nível do sistema operacional sem exigir um callback out-of-band.
Pré-requisitos:
pip install requests
Executar o exploit:
python3 exploit.py [--target http://localhost:8082] [--wait]

Docker Desktop (macOS/Windows): use host.docker.internal em vez de 127.0.0.1 para payloads de callback.
O PoC executa cinco etapas sequenciais:
| Etapa | Descrição |
|---|---|
| 1 | Verificação de linha de base — confirma que o endpoint está acessível e retorna dados semeados |
| 2 | Sonda SpEL cega — lê java.version via T(java.lang.System) para confirmar o ponto de injeção |
| 3 | RCE: touch /tmp/pwned_cve_2026_22738 dentro do contêiner |
| 4 | RCE: grava a saída de id / uname / hostname em /tmp/rce_proof.txt |
| 5 | Verificação via docker exec — imprime o conteúdo do arquivo de prova |
Esta prova de conceito é fornecida apenas para fins educacionais e de testes de segurança autorizados. Não use contra sistemas que você não possui ou para os quais não tenha permissão explícita por escrito para testar.