Spring 視圖操縱漏洞
聲明
由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,雷神眾測以及文章作者不為此承擔任何責任。
雷神眾測擁有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經雷神眾測允許,不得任意修改或者增減此文章內容,不得以任何方式將其用于商業目的。
No.1 概述
知識點起源于一個小哥的GitHub的demo,已經在 R**eference** 提及了,核心觀點想要表達如果能操縱spring的視圖(view)是一件很危險的事情,緊接著用到了 Thymeleaf 這個模版來舉例子。
No.2 跟蹤過程
小哥在代碼中舉了兩個例子,這兩個例子分別是由相同的特征,返回的內容可被攻擊者操縱。
//GET /path?lang=en HTTP/1.1
這里先慢慢看,spring的模版處理在這里 *org.springframework.web.servlet.ViewView# render *,根據注釋可以知道這個地方是個接口,要實現需要到相關模版渲染引擎單中去實現。
/**
而實際上的處理過程是在 org.springframework.web.servlet.DispatcherServlet.render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
跟一下流程,首先在String viewName = mv.getViewName();的過程中就獲取到我們傳入的POC。

緊接著進行view = resolveViewName(viewName, mv.getModelInternal(), locale, request);處理,這個 resolveViewName ,當從英文翻譯就知道它大概要解析視圖名字 ,當然本著嚴謹的角度還是需要看代碼的,跟進來之后會來到 ContentNegotiatingViewResolver# resolveViewName *當中,關注一下 *getCandidateViews
public View resolveViewName(String viewName, Locale locale) throws Exception {
先跟進來 getCandidateViews ,這玩意會循環當前的 *this.viewResolvers *內容,并且進行處理。

當解析到 ThymeleafViewResolver *這個方法的時候,會進入 *createView ,在這個方法中根據幾個條件進行判斷,比如redirect:或者forward:等等。因此我們的 ViewName *不滿足,自然是在 *resolveViewName 處理之后返回了*ThymeleafView *。

同理在** UrlBasedViewResolver# createView** 方法當中也是大概根據redirect:或者forward:進行判斷,因此在** resolveViewName** 處理之后返回了*InternalResourceView *。

再看 getBestView,我們當前的 candidateViews *這個 *list *當中有兩個view分別是 *InternalResourceView 和 ThymeleafView ,進入處理之后返回的是** ThymeleafView** 。也就是說經過view = resolveViewName(viewName, mv.getModelInternal(), locale, request);處理之后,返回了我們的視圖方法是 ThymeleafView ,緊接著就是view.render(mv.getModelInternal(), request, response);來到相關的視圖方法中進行解析了。

當然最后的模版解析過程在 org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator# evaluate 。
exec:347, Runtime (java.lang)
No.3 區分
1、加上@ResponseBody注釋
//GET /fragment?section=main
首先需要了解 *@ResponseBody *注釋的作用。
1、該注解用于讀取Request請求的body部分數據,使用系統默認配置的HttpMessageConverter進行解析,然后把相應的數據綁定到要返回的對象上;
而實際上在** RequestMappingHandlerAdapter# handleInternal *返回view的對象的時候返回空,找不到模版對象,自然也就沒有下面的模版解析 *render **的渲染過程了。

2、redirect:或者forward:前綴
@GetMapping("/safe/redirect")
實際上我們前面也聊過了在** AbstractCachingViewResolver# resolveViewName 當中,當調用view = createView(viewName, locale);會進入每個模版的 createView** 方法,而這個方法根據前綴redirect:或者forward:進行匹配,返回的是 RedirectView 或者** InternalResourceView** ,所以自然不會進行模版解析。


3、HttpServletResponse標記
在構造方法中指定HttpServletResponse response的org/springframework/web/servlet/DispatcherServlet# doDispatch **方法之后,實際上就不返回 modelandView ,**也就是說實際上沒有進行模版解析。
@GetMapping("/doc/{document}")

No.4 后話
那么 velocity 和** freemarker **有這個問題嗎,目前我還沒發現,似乎傳入進來不會做表達式解析,可以順著這個思路再看看,總體來說還是挺有趣的。

而** Thymeleaf *傳入進來之后在 *ThymeleafView# renderFragment **會針對傳入的進行表達式解析。


當然一般開發的時候,好像是不會這么處理視圖的,因此這么寫的實際面應該不會太大,當然和** spring** 解析過程有關方法調試斷點可以下在** DispatcherServlet# doDispatch **,一般都是可以命中的。
No.5 Reference
https://github.com/veracode-research/sprin...
原創:l1nk3r 雷神眾測
鏈接:https://mp.weixin.qq.com/s/pJGE7nS2zg-tuz4...