在本節中,咱們將介紹什麼是服務端模板注入,並概述利用此漏洞的基本方法,同時也將提供一些避免此漏洞的建議。java
服務端模板注入是指攻擊者可以利用模板自身語法將惡意負載注入模板,而後在服務端執行。web
模板引擎被設計成經過結合固定模板和可變數據來生成網頁。當用戶輸入直接拼接到模板中,而不是做爲數據傳入時,可能會發生服務端模板注入攻擊。這使得攻擊者可以注入任意模板指令來操縱模板引擎,從而可以徹底控制服務器。顧名思義,服務端模板注入有效負載是在服務端交付和執行的,這可能使它們比典型的客戶端模板注入更危險。shell
服務端模板注入漏洞會使網站面臨各類攻擊,具體取決於所討論的模板引擎以及應用程序如何使用它。在極少數狀況下,這些漏洞不會帶來真正的安全風險。然而,大多數狀況下,服務端模板注入的影響多是災難性的。後端
最嚴重的狀況是,攻擊者有可能完成遠程代碼執行,從而徹底控制後端服務器,並利用它對內部基礎設施進行其餘攻擊。安全
即便在不可能徹底執行遠程代碼的狀況下,攻擊者一般仍可使用服務端模板注入做爲許多其餘攻擊的基礎,從而可能得到服務器上敏感數據和任意文件的訪問權限。ruby
當用戶輸入直接拼接到模板中而不是做爲數據傳入時,就會出現服務端模板注入漏洞。服務器
簡單地提供佔位符並在其中呈現動態內容的靜態模板一般不會受到服務端模板注入的攻擊。典型的例子如提取用戶名做爲電子郵件的開頭,例如如下從 Twig 模板中提取的內容:編輯器
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
這不容易受到服務端模板注入的攻擊,由於用戶的名字只是做爲數據傳遞到模板中的。ide
可是,Web 開發人員有時可能將用戶輸入直接鏈接到模板中,如:函數
$output = $twig->render("Dear " . $_GET['name']);
此時,不是將靜態值傳遞到模板中,而是使用 GET name 動態生成模板自己的一部分。因爲模板語法是在服務端執行的,這可能容許攻擊者使用 name 參數以下:
http://vulnerable-website.com/?name={{bad-stuff-here}}
像這樣的漏洞有時是因爲不熟悉安全概念的人設計了有缺陷的模板形成的。與上面的例子同樣,你可能會看到不一樣的組件,其中一些組件包含用戶輸入,鏈接並嵌入到模板中。在某些方面,這相似於 SQL 注入漏洞,都是編寫了不當的語句。
然而,有時這種行爲其實是有意爲之。例如,有些網站故意容許某些特權用戶(如內容編輯器)經過設計來編輯或提交自定義模板。若是攻擊者可以利用特權賬戶,這顯然會帶來巨大的安全風險。
識別服務端模板注入漏洞並策劃成功的攻擊一般涉及如下抽象過程。
服務端模板注入漏洞經常不被注意到,這不是由於它們很複雜,而是由於它們只有在明確尋找它們的審計人員面前才真正明顯。若是你可以檢測到存在漏洞,則利用它將很是容易。在非沙盒環境中尤爲如此。
與任何漏洞同樣,利用漏洞的第一步就是先找到它。也許最簡單的初始方法就是注入模板表達式中經常使用的一系列特殊字符,例如 ${{<%[%'"}}%\
,去嘗試模糊化模板。若是引起異常,則代表服務器可能以某種方式解釋了注入的模板語法,從而代表服務端模板注入可能存在漏洞。
服務端模板注入漏洞發生在兩個不一樣的上下文中,每一個上下文都須要本身的檢測方法。無論模糊化嘗試的結果如何,也要嘗試如下特定於上下文的方法。若是模糊化是不肯定的,那麼使用這些方法之一,漏洞可能會暴露出來。即便模糊化確實代表存在模板注入漏洞,你仍然須要肯定其上下文才能利用它。
純文本上下文。
大多數模板語言容許你經過直接使用 HTML tags 或模板語法自由地輸入內容,後端在發送 HTTP 響應以前,會把這些內容渲染爲 HTML 。例如,在 Freemarker 模板中,render('Hello ' + username)
可能會渲染爲 Hello Carlos
。
這有時常常被誤認爲是一個簡單的 XSS 漏洞並用於 XSS 攻擊。可是,經過將數學運算設置爲參數的值,咱們能夠測試其是否也是服務端模板注入攻擊的潛在攻擊點。
例如,考慮包含如下模板代碼:
render('Hello ' + username)
在審查過程當中,咱們能夠經過請求如下 URL 來測試服務端模板注入:
http://vulnerable-website.com/?username=${7*7}
若是結果輸出包含 Hello 49
,這代表數學運算被服務端執行了。這是服務端模板注入漏洞的一個很好的證實。
請注意,成功計算數學運算所需的特定語法將因使用的模板引擎而異。咱們將在 Identify 步驟詳細說明。
代碼上下文。
在其餘狀況下,漏洞暴露是由於將用戶輸入放在了模板表達式中,就像上文中的電子郵件示例中看到的那樣。這能夠採用將用戶可控制的變量名放置在參數中的形式,例如:
greeting = getQueryParameter('greeting') engine.render("Hello {{"+greeting+"}}", data)
在網站上生成的 URL 相似於:
http://vulnerable-website.com/?greeting=data.username
渲染的輸出可能爲 Hello Carlos
。
在評估過程當中很容易忽略這個上下文,由於它不會產生明顯的 XSS,而且與簡單的 hashmap 查找幾乎沒有區別。在這種狀況下,測試服務端模板注入的一種方法是首先經過向值中注入任意 HTML 來肯定參數不包含直接的 XSS 漏洞:
http://vulnerable-website.com/?greeting=data.username<tag>
在沒有 XSS 的狀況下,這一般會致使輸出中出現空白(只有 Hello,沒有 username ),編碼標籤或錯誤信息。下一步是嘗試使用通用模板語法來跳出該語句,並嘗試在其後注入任意 HTML :
http://vulnerable-website.com/?greeting=data.username}}<tag>
若是這再次致使錯誤或空白輸出,則說明你使用了錯誤的模板語法。或者,模板樣式的語法均無效,此時則沒法進行服務端模板注入。若是輸出與任意 HTML 一塊兒正確呈現,則這是服務端模板注入漏洞存在的關鍵證實:
Hello Carlos<tag>
一旦檢測到潛在的模板注入,下一步就是肯定模板引擎。
儘管有大量的模板語言,但許多都使用很是類似的語法,這些語法是專門爲避免與 HTML 字符衝突而選擇的。所以,構造試探性載荷來測試正在使用哪一個模板引擎可能相對簡單。
簡單地提交無效的語法就足夠了,由於生成的錯誤消息會告訴你用了哪一個模板引擎,有時甚至能具體到哪一個版本。例如,非法的表達式 <%=foobar%>
觸發了基於 Ruby 的 ERB 引擎的以下響應:
(erb):1:in `<main>': undefined local variable or method `foobar' for main:Object (NameError) from /usr/lib/ruby/2.5.0/erb.rb:876:in `eval' from /usr/lib/ruby/2.5.0/erb.rb:876:in `result' from -e:4:in `<main>'
不然,你將須要手動測試不一樣語言特定的有效負載,並研究模板引擎如何解釋它們。使用基於語法有效或無效的排除過程,你能夠比你想象的更快地縮小選項範圍。一種常見的方法是使用來自不一樣模板引擎的語法注入任意的數學運算。而後,觀察它們是否被成功執行。要完成此過程,可使用相似於如下內容的決策樹:
你應該注意,一樣的有效負載有時能夠得到多個模板語言的成功響應。例如,有效載荷 {{7*'7'}}
在 Twig
中返回 49
,在 Jinja2
中返回 7777777
。所以,不要只由於成功響應了就草率下結論。
在檢測到存在潛在漏洞併成功識別模板引擎以後,就能夠開始嘗試尋找利用它的方法。詳細請翻閱下文。
防止服務端模板注入的最佳方法是不容許任何用戶修改或提交新模板。然而,因爲業務需求,這有時是不可避免的。
避免引入服務端模板注入漏洞的最簡單方法之一是,除非絕對必要,始終使用「無邏輯」模板引擎,如 Mustache
。儘量的將邏輯與表示分離,這能夠大大減小高危險性的基於模板的攻擊的風險。
另外一措施是僅在徹底刪除了潛在危險模塊和功能的沙盒環境中執行用戶的代碼。不幸的是,對不可信的代碼進行沙盒處理自己就很困難,並且容易被繞過。
最後,對於接受任意代碼執行沒法避免的狀況,另外一種補充方法是,經過在鎖定的例如 Docker 容器中部署模板環境,來應用你本身的沙盒。
在本節中,咱們將更仔細地瞭解一些典型的服務端模板注入漏洞,並演示如何利用以前概括的方法。經過付諸實踐,你能夠潛在地發現和利用各類不一樣的服務端模板注入漏洞。
一旦發現服務端模板注入漏洞,並肯定正在使用的模板引擎,成功利用該漏洞一般涉及如下過程。
閱讀
除非你已經對模板引擎瞭如指掌,不然應該先閱讀其文檔。雖然這可能有點無聊,可是不要低估文檔多是有用的信息來源。
學習基本語法、關鍵函數和變量處理顯然很重要。即便只是簡單地學習如何在模板中嵌入本機代碼塊,有時也會很快致使漏洞利用。例如,一旦你知道正在使用基於 Python 的 Mako 模板引擎,實現遠程代碼執行能夠簡單到:
<% import os x=os.popen('id').read() %> ${x}
在非沙盒環境中,實現遠程代碼執行並將其用於讀取、編輯或刪除任意文件在許多常見模板引擎中都很是簡單。
除了提供如何建立和使用模板的基礎知識外,文檔還可能提供某種「安全」部分。這個部分的名稱會有所不一樣,但它一般會歸納出人們應該避免使用模板進行的全部潛在危險的事情。這多是一個很是寶貴的資源,甚至能夠做爲一種備忘單,爲你應該尋找哪些行爲,以及如何利用它們提供指南。
即便沒有專門的「安全」部分,若是某個特定的內置對象或函數會帶來安全風險,文檔中幾乎老是會出現某種警告。這個警告可能不會提供太多細節,但至少應將其標記爲能夠深刻挖掘研究的內容。
例如,在 ERB 模板中,文檔顯示能夠列出全部目錄,而後按以下方式讀取任意文件:
<%= Dir.entries('/') %> <%= File.open('/example/arbitrary-file').read %>
利用服務端模板注入漏洞的另外一個關鍵方面是善於查找其餘在線資源。一旦你可以識別正在使用的模板引擎,你應該瀏覽 web 以查找其餘人可能已經發現的任何漏洞。因爲一些主要模板引擎的普遍使用,有時可能會發現有充分記錄的漏洞利用,你能夠對其進行調整以利用到本身的目標網站。
此時,你可能已經在使用文檔時偶然發現了一個可行的漏洞利用。若是沒有,下一步就是探索環境並嘗試發現你能夠訪問的全部對象。
許多模板引擎公開某種類型的 self
或 environment
對象,其做用相似於包含模板引擎支持的全部對象、方法和屬性的命名空間。若是存在這樣的對象,則能夠潛在地使用它來生成範圍內的對象列表。例如,在基於 Java 的模板語言中,有時可使用如下注入列出環境中的全部變量:
${T(java.lang.System).getenv()}
這能夠做爲建立一個潛在有趣對象和方法的短名單的基礎,以便進一步研究。
須要注意的是,網站將包含由模板提供的內置對象和由 web 開發人員提供的自定義、特定於站點的對象。你應該特別注意這些非標準對象,由於它們特別可能包含敏感信息或可利用的方法。因爲這些對象可能在同一網站中的不一樣模板之間有所不一樣,請注意,你可能須要在每一個不一樣模板的上下文中研究對象的行爲,而後才能找到利用它的方法。
雖然服務端模板注入可能致使遠程代碼執行和服務器的徹底接管,但在實踐中,這並不是老是能夠實現。然而,僅僅排除了遠程代碼執行,並不必定意味着不存在其餘類型的攻擊。你仍然能夠利用服務端模板注入漏洞進行其餘高危害性攻擊,例如目錄遍歷,以訪問敏感數據。
到目前爲止,咱們主要研究了經過重用已記錄的漏洞攻擊或使用模板引擎中已知的漏洞來構建攻擊。可是,有時你須要構建一個自定義的漏洞利用。例如,你可能會發現模板引擎在沙盒中執行模板,這會使攻擊變得困難,甚至不可能。
在識別攻擊點以後,若是沒有明顯的方法來利用漏洞,你應該繼續使用傳統的審計技術,檢查每一個函數的可利用行爲。經過有條不紊地完成這一過程,你有時能夠構建一個複雜的攻擊,甚至可以利用於更安全的目標。
如上文所述,第一步是標識你有權訪問的對象和方法。有些對象可能會當即跳出來。經過結合你本身的知識和文檔中提供的信息,你應該可以將你想要更完全地挖掘的對象的短名單放在一塊兒。
在研究對象的文檔時,要特別注意這些對象容許訪問哪些方法,以及它們返回哪些對象。經過深刻到文檔中,你能夠發現能夠連接在一塊兒的對象和方法的組合。將正確的對象和方法連接在一塊兒有時容許你訪問最初看起來高不可攀的危險功能和敏感數據。
例如,在基於 Java 的模板引擎 Velocity 中,你能夠調用 $class
訪問 ClassTool
對象。研究文檔代表,你能夠鏈式使用 $class.inspect()
方法和 $class.type
屬性引用任意對象。在過去,這被用來在目標系統上執行 shell 命令,以下所示:
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")
一些模板引擎默認運行在安全、鎖定的環境中,以便儘量地下降相關風險。儘管這使得利用這些模板進行遠程代碼執行變得很困難,可是開發人員建立的暴露於模板的對象能夠提供更進一步的攻擊點。
然而,雖然一般爲模板內置對象提供了大量的文檔,可是網站特定的對象幾乎根本就沒有文檔記錄。所以,要想知道如何利用這些漏洞,就須要你手動調查網站的行爲,以肯定攻擊點,並據此構建你本身的自定義攻擊。