服務端模板注入攻擊 (SSTI) 之淺析

wyzsk發表於2020-08-19
作者: RickGray · 2015/11/05 11:50

Author: RickGray (知道創宇404安全實驗室)

在今年的黑帽大會上 James Kettle 講解了 《Server-Side Template Injection: RCE for the modern webapp》,從服務端模板注入的形成到檢測,再到驗證和利用都進行了詳細的介紹。本文在理解原文內容的基礎上,結合更為具體的示例對服務端模板注入的原理和掃描檢測方法做一個淺析。

0x00 模板注入與常見Web注入


就注入型別的漏洞來說,常見 Web 注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,程式碼注入,命令注入等等。注入漏洞的實質是服務端接受了使用者的輸入,未過濾或過濾不嚴謹執行了拼接了使用者輸入的程式碼,因此造成了各類注入。下面這段程式碼足以說明這一點:

#!php
// SQL 注入
$query = "select * from sometable where id=".$_GET['id'];
mysql_query($query);    

------------- 華麗的分割線 -------------    

// 模版注入
$temp->render("Hello ".$_GET['username']);

而服務端模板注入和常見Web注入的成因一樣,也是服務端接收了使用者的輸入,將其作為 Web 應用模板內容的一部分,在進行目標編譯渲染的過程中,執行了使用者插入的惡意內容,因而可能導致了敏感資訊洩露、程式碼執行、GetShell 等問題。其影響範圍主要取決於模版引擎的複雜性。

0x01 模板注入原理


模板注入涉及的是服務端Web應用使用模板引擎渲染使用者請求的過程,這裡我們使用 PHP 模版引擎 Twig 作為例子來說明模板注入產生的原理。考慮下面這段程式碼:

#!php
<?php
require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';
Twig_Autoloader::register(true);    

$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {{name}}", array("name" => $_GET["name"]));  // 將使用者輸入作為模版變數的值
echo $output;

使用 Twig 模版引擎渲染頁面,其中模版含有 {{name}} 變數,其模版變數值來自於 GET 請求引數 $_GET["name"]。顯然這段程式碼並沒有什麼問題,即使你想透過 name 引數傳遞一段 JavaScript 程式碼給服務端進行渲染,也許你會認為這裡可以進行 XSS,但是由於模版引擎一般都預設對渲染的變數值進行編碼和轉義,所以並不會造成跨站指令碼攻擊:

但是,如果渲染的模版內容受到使用者的控制,情況就不一樣了。修改程式碼為:

#!php
<?php
require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';
Twig_Autoloader::register(true);    

$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {$_GET['name']}");  // 將使用者輸入作為模版內容的一部分
echo $output;

上面這段程式碼在構建模版時,拼接了使用者輸入作為模板的內容,現在如果再向服務端直接傳遞 JavaScript 程式碼,使用者輸入會原樣輸出,測試結果顯而易見:

對比上面兩種情況,簡單的說服務端模板注入的形成終究還是因為服務端相信了使用者的輸出而造成的(Web安全真諦:永遠不要相信使用者的輸入!)。當然了,第二種情況下,攻擊者不僅僅能插入 JavaScript 指令碼,還能針對模板框架進行進一步的攻擊,此部分只說明原理,在後面會對攻擊利用進行詳細說明和演示。

0x02 模板注入檢測


上面已經講明瞭模板注入的形成原來,現在就來談談對其進行檢測和掃描的方法。如果服務端將使用者的輸入作為了模板的一部分,那麼在頁面渲染時也必定會將使用者輸入的內容進行模版編譯和解析最後輸出。

借用本文第二部分所用到的程式碼:

#!php
<?php
require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';
Twig_Autoloader::register(true);    

$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {$_GET['name']}");  // 將使用者輸入作為模版內容的一部分
echo $output;

在 Twig 模板引擎裡,{{ var }} 除了可以輸出傳遞的變數以外,還能執行一些基本的表示式然後將其結果作為該模板變數的值,例如這裡使用者輸入 name={{2*10}},則在服務端拼接的模版內容為:

Hello {{2*10}}

Twig 模板引擎在編譯模板的過程中會計算 {{2*10}} 中的表示式 2*10,會將其返回值 20 作為模板變數的值輸出,如下圖:

現在把測試的資料改變一下,插入一些正常字元和 Twig 模板引擎預設的註釋符,構造 Payload 為:

IsVuln{# comment #}{{2*8}}OK

實際服務端要進行編譯的模板就被構造為:

Hello IsVuln{# comment #}{{2*8}}OK

這裡簡單分析一下,由於 {# comment #} 作為 Twig 模板引擎的預設註釋形式,所以在前端輸出的時候並不會顯示,而 {{2*8}} 作為模板變數最終會返回 16 作為其值進行顯示,因此前端最終會返回內容 Hello IsVuln16OK,如下圖:

透過上面兩個簡單的示例,就能得到 SSTI 掃描檢測的大致流程(這裡以 Twig 為例):

同常規的 SQL 注入檢測,XSS 檢測一樣,模板注入漏洞的檢測也是向傳遞的引數中承載特定 Payload 並根據返回的內容來進行判斷的。每一個模板引擎都有著自己的語法,Payload 的構造需要針對各類别範本引擎制定其不同的掃描規則,就如同 SQL 注入中有著不同的資料庫型別一樣。

簡單來說,就是更改請求引數使之承載含有模板引擎語法的 Payload,透過頁面渲染返回的內容檢測承載的 Payload 是否有得到編譯解析,有解析則可以判定含有 Payload 對應模板引擎注入,否則不存在 SSTI。

0x03 小結


本文簡單介紹服務端模版注入漏洞如常規 Web 注入漏洞之間的關係,分析了其產生的原理,並以 PHP 模板引擎 Twig 為例講解了 SSTI 常規的掃描和檢測方法。雖然說 SSTI 並不廣泛存在,但如果開發人員濫用模板引擎,進行不安全的編碼,這樣 Web 應用就可能出現 SSTI,並且根據其模板引擎的複雜性和開發語言的特性,可能會造成非常嚴重的問題。

在後續的文章中會針對各語言流行的模板引擎做一個較為詳細的研究和分析,給出對應的可利用點和方法。

0x04 參考


原文出處:http://blog.knownsec.com/2015/11/server-side-template-injection-attack-analysis/

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章