您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何進行Confluence未授權RCE的漏洞分析,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
看到官方發布了預警,于是開始了漏洞應急。漏洞描述中指出Confluence Server與Confluence數據中心中的Widget連接器存在服務端模板注入漏洞,攻擊者能利用此漏洞能夠實現目錄穿越與遠程代碼執行。
確認漏洞點是Widget Connector,下載最新版的比對補丁,發現在com\atlassian\confluence\extra\widgetconnector\WidgetMacro.java
里面多了一個過濾,這個應該就是這個漏洞最關鍵的地方。
可以看到
this.sanitizeFields = Collections.unmodifiableList(Arrays.asList(VelocityRenderService.TEMPLATE_PARAM));
而TEMPLATE_PARAM
的值就是_template
,這個所以就是補丁了過濾傳入外部的_template
參數。
public interface VelocityRenderService { public static final String WIDTH_PARAM = "width"; public static final String HEIGHT_PARAM = "height"; public static final String TEMPLATE_PARAM = "_template";
翻了一下Widget Connector里面的文件,發現TEMPLATE_PARAM
就是模板文件的路徑。
public class FriendFeedRenderer implements WidgetRenderer {
private static final String MATCH_URL = "friendfeed.com";
private static final String PATTERN = "friendfeed.com/(\\w+)/?";
private static final String VELOCITY_TEMPLATE = "com/atlassian/confluence/extra/widgetconnector/templates/simplejscript.vm";
private VelocityRenderService velocityRenderService;
......
public String getEmbeddedHtml(String url, Map<String, String> params) {
params.put(VelocityRenderService.TEMPLATE_PARAM, VELOCITY_TEMPLATE);
return velocityRenderService.render(getEmbedUrl(url), params);
}
加載外部的鏈接時,會調用相對的模板去渲染,如上,模板的路徑一般是寫死的,但是也有例外,補丁的作用也說明有人突破了限制,調用了意料之外的模板,從而造成了模板注入。
在了解了補丁和有了一些大概的猜測之后,開始嘗試。
首先先找到這個功能,翻了一下官方的文檔,找到了這個功能,可以在文檔中嵌入一些視頻,文檔之類的。
看到這個,有點激動了,因為在翻補丁的過程中,發現了幾個參數,url
,width
,height
正好對應著這里,那_template
的英文不是也。從這里傳遞進去的?
隨便找個的Youtube視頻插入試試,點擊預覽,抓包。
在params
中嘗試插入_template
參數,好吧,沒啥反應..
開始調試模式,因為測試插入的是的Youtube視頻,所以調用的是com/atlassian/confluence/extra/widgetconnector/video/YoutubeRenderer.class
public class YoutubeRenderer implements WidgetRenderer, WidgetImagePlaceholder {private static final Pattern YOUTUBE_URL_PATTERN = Pattern.compile("https?://(.+\\.)?youtube.com.*(\\?v=([^&]+)).*$");private final PlaceholderService placeholderService;private final String DEFAULT_YOUTUBE_TEMPLATE = "com/atlassian/confluence/extra/widgetconnector/templates/youtube.vm";......public String getEmbedUrl(String url) {Matcher youtubeUrlMatcher = YOUTUBE_URL_PATTERN.matcher(this.verifyEmbeddedPlayerString(url));return youtubeUrlMatcher.matches() ? String.format("//www.youtube.com/embed/%s?wmode=opaque", youtubeUrlMatcher.group(3)) : null; }public boolean matches(String url) {return YOUTUBE_URL_PATTERN.matcher(this.verifyEmbeddedPlayerString(url)).matches();}private String verifyEmbeddedPlayerString(String url) {return !url.contains("feature=player_embedded&") ? url : url.replace("feature=player_embedded&", "");}public String getEmbeddedHtml(String url, Map<String, String> params) {return this.velocityRenderService.render(this.getEmbedUrl(url), this.setDefaultParam(params));}
在getEmbeddedHtml
下斷點,會先調用getEmbedUrl
對用戶傳入的url
進行正則匹配,因為我們傳入的是個正常的的Youtube視頻,所以這里是沒有問題的,調用然后setDefaultParam
函數對傳入的其他參數進行處理。
private Map<String, String> setDefaultParam(Map<String, String> params) {String width = (String)params.get("width");String height = (String)params.get("height");if (!params.containsKey("_template")) {params.put("_template", "com/atlassian/confluence/extra/widgetconnector/templates/youtube.vm");}if (StringUtils.isEmpty(width)) {params.put("width", "400px");} else if (StringUtils.isNumeric(width)) {params.put("width", width.concat("px"));}if (StringUtils.isEmpty(height)) {params.put("height", "300px");} else if (StringUtils.isNumeric(height)) {params.put("height", height.concat("px"));}return params;}
取出width
狀語從句:height
來判斷是否為空,為空則設置默認值。的關鍵_template
參數來了,如果外部傳入的參數沒有_template
,則設置默認的的Youtube模板。如果傳入了,就使用傳入的,也就是說,AAAA是成功的傳進來了。
大概翻了一下Widget Connector里面的渲染器,大部分是不能設置_template
的,是直接寫死了,也有一些例外,如Youtube,Viddler,DailyMotion等,是可以從外部傳入_template
的。
傳遞能_template
了,接下來看下是如何取模板和渲染模板的。
跟進this.velocityRenderService.render
,就是也。com/atlassian/confluence/extra/widgetconnector/services/DefaultVelocityRenderService.class
里面的render
方法。
public String render(String url, Map<String, String> params) {String width = (String)params.get("width");String height = (String)params.get("height");String template = (String)params.get("_template");if (StringUtils.isEmpty(template)) {template = "com/atlassian/confluence/extra/widgetconnector/templates/embed.vm";}if (StringUtils.isEmpty(url)) {return null;} else {Map<String, Object> contextMap = this.getDefaultVelocityContext();Iterator var7 = params.entrySet().iterator();while(var7.hasNext()) {Entry<String, String> entry = (Entry)var7.next();if (((String)entry.getKey()).contentEquals("tweetHtml")) {contextMap.put(entry.getKey(), entry.getValue());} else {contextMap.put(entry.getKey(), GeneralUtil.htmlEncode((String)entry.getValue()));}}contextMap.put("urlHtml", GeneralUtil.htmlEncode(url));if (StringUtils.isNotEmpty(width)) {contextMap.put("width", GeneralUtil.htmlEncode(width));} else {contextMap.put("width", "400");}if (StringUtils.isNotEmpty(height)) {contextMap.put("height", GeneralUtil.htmlEncode(height));} else {contextMap.put("height", "300");}return this.getRenderedTemplate(template, contextMap);}}
_template
取出來賦值給template
,其他傳遞進來的參數取出來經過判斷之后放入到contextMap
,調用getRenderedTemplate
函數,也就是調用VelocityUtils.getRenderedTemplate
。
protected String getRenderedTemplate(String template, Map<String, Object> contextMap){return VelocityUtils.getRenderedTemplate(template, contextMap);}
一路調用,調用鏈如下圖,來到求最后/com/atlassian/confluence/util/velocity/ConfigurableResourceManager.class
的loadResource
函數,來獲取模板。
這里調用了4個ResourceLoader
去取模板。
com.atlassian.confluence.setup.velocity.HibernateResourceLoader org.apache.velocity.runtime.resource.loader.FileResourceLoader org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader com.atlassian.confluence.setup.velocity.DynamicPluginResourceLoader
這里主要看下速度的自帶FileResourceLoader
狀語從句:ClasspathResourceLoader
FileResourceLoader
用戶會對傳入的模板路徑使用normalizePath
函數進行校驗
可以看到,過濾了/../
,這樣就導致沒有辦法跳目錄了。
過濾路徑后調用findTemplate
查找模板,可看到,會拼接一個固定的path
,這是合流的安裝路徑。
現在也就是說可以利用FileResourceLoader
來讀取合流目錄下面的文件了。
讀取嘗試/WEB-INF/web.xml
文件,可以看到,是成功的加載到了該文件。
但是這個無法跳出合流的目錄,因為不能用/../
。
再來看下ClasspathResourceLoader
public InputStream getResourceStream(String name) throws ResourceNotFoundException {
InputStream result = null;
if (StringUtils.isEmpty(name)) {
throw new ResourceNotFoundException("No template name provided");
} else {
try {
result = ClassUtils.getResourceAsStream(this.getClass(), name);
......
}
跟進ClassUtils.getResourceAsStream
public static InputStream getResourceAsStream(Class claz, String name) {while(name.startsWith("/")) {name = name.substring(1);}ClassLoader classLoader = Thread.currentThread().getContextClassLoader();InputStream result;if (classLoader == null) {classLoader = claz.getClassLoader();result = classLoader.getResourceAsStream(name);} else {result = classLoader.getResourceAsStream(name);if (result == null) {classLoader = claz.getClassLoader();if (classLoader != null) {result = classLoader.getResourceAsStream(name);}}}return result;}
會跳到/org/apache/catalina/loader/WebappClassLoaderBase.class
跟進,發現會拼接/WEB-INF/classes
,其中而且也是調用了normalize
對傳入的路徑進行過濾..
還是這里可以用../
跳一級目錄。
嘗試讀取一下../web.xml
,可以看到,也是可以讀取成功的,但是仍然無法跳出目錄。
我這里測試用的版本是6.14.1
,而后嘗試了file://
,http://
,https://
都沒有成功。后來我嘗試把曲奇刪掉,發現還是可以讀取文件,確認了這個漏洞不需要權限,但是跳不出目錄。應急就在這里卡住了。
而后的幾天,大佬有說用file://
協議可以跳出目錄限制,我驚了,我確定當時是已經試過了,沒有成功的。看了大佬的截圖,發現用的是6.9.0的版本,我下載了,嘗試了一下,發現真的可以。
還是問題在ClasspathResourceLoader
上面,步驟和之前的是一樣的,到斷/org/apache/catalina/loader/WebappClassLoaderBase.class
的getResourceAsStream
方法
拼接前面/WEB-INF/classes
電子雜志失敗后,繼續往下進行。
跟進findResource
,函數前面仍然獲取失敗
關鍵的地方就在這里,會調用super.findResource(name)
,這里返回了網址,也就是能獲取到對象。
不僅如此,這里還可以使用其他協議(HTTPS,FTP等)獲取遠程的對象,意味著可以加載遠程的對象。
獲取到URL對象之后,繼續回到之前的getResourceAsStream
,可以看到,當返回的URL不為空時,
調用會url.openStream()
電子雜志數據。
最終獲取到數據給速度渲染。
嘗試一下
至于6.14.1為啥不行,趕著應急,后續會跟,如果有新的發現,會同步上來,只目前看到ClassLoader
不一樣。
6.14.1
6.9.0
這兩個裝載機的關系如下
現在可以加載本地和遠程模板了,可以嘗試進行RCE。
關于速度的RCE,基本上有效載荷都來源于15年入侵者的服務端模板注入的議題,但是在合流上用不了,因為在調用方法的時候會經過velocity-htmlsafe-1.5.1.jar
,里面多了一些過濾和限制。但是仍然可以利用反射來執行命令。
用python -m pyftpdlib -p 2121
開啟一個簡單的FTP服務器,將有效載荷保存成rce.vm,保存在當前目錄。
將_template
設置分類中翻譯ftp://localhost:2121/rce.vm
,發送,成功執行命令。
對于命令回顯,同樣可以使用反射構造出有效載荷,執行ipconfig
的查詢查詢結果。
根據ZoomEye網絡空間搜索引擎對關鍵字“X-Confluence”進行搜索,共得到61,856條結果,主要分布美國,德國,中國等國家。
全球分布(非漏洞影響范圍)
中國分布(非漏洞影響范圍)
2019年4月4日,404實驗室公布了該漏洞的檢測PoC,可以利用這個PoC檢測Confluence是否受該漏洞影響。
關于如何進行Confluence未授權RCE的漏洞分析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。