
keep going🏃♀️,有了前面的基础,这个应该不是很难,但是要学习新的java知识
环境
jdk8u65
commons-beanutils 1.9.2
参考Drunkbaby师傅给的环境
1 | <dependency> |
CommonsBeanUtils介绍
CommonsBeanUtils是一个Apache库里的工具,用来简化对 Java Bean 的操作,比如
- 反射获取类的属性
- 动态设置属性值
- 类型转换
- Bean 之间的属性拷贝
Java Bean 是符合特定规范的 Java 类,主要用于封装数据。其核心特征包括:
- 必须有无参构造方法(可通过默认构造器或显式定义)。
- 属性私有化(
private
修饰),通过公共的getter
和setter
方法访问。可以利用IDE快速生成getter
和setter
- 可序列化(实现
Serializable
接口,非强制但常见)。
写一个JavaBean
1 | package com; |
示例写一个User类,然后用Commons-BeanUtils 的PropertyUtils.getProperty
静态方法,让使用者可以直接调用任意 JavaBean 的 getter 方法
PropertyUtils.getProperty()
传入两个参数,第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性,刚刚的
1 | PropertyUtils.getProperty(user, "name"); |
PropertyUtils.getProperty` 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过PropertyUtils.getProperty(a,”b.c”); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter
除了这个CommonsBeanUtils还有很多方法可以对JavaBean进行操作,等后续再接触
CommonsBeanUtils链分析
已知这条链子最后用的是恶意类加载,之前用到的链子尾部是
1 | TemplatesImpl#getOutputProperties() |
说是getOutputProperties
算是一个getter方法,问了一下GPT,确实都符合条件
在 JavaBean规范 中,getter 方法的特点是:
1.方法名以 get 开头(或者对于 boolean 类型是 is 开头)。
2.后面跟着属性名的首字母大写版本。
3.必须是 public 访问权限。
4.返回一个属性的值。
所以我们可以用PropertyUtils.getProperty()
获取到,大概长这样
1 | PropertyUtils.getProperty(templates,"outputProperties"); |
那现在看谁调用了PropertyUtils.getProperty()
,找到一个BeanComparator.compare()
,说到compare就熟了,前面CC4接触过,那就接上了!画了个图,看起来就是殊途同归
CommonsBeanUtils链EXP编写
1 | public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException { |
卡在这里,在思考要怎么把templates作为o1传入compare,这里可以先运行尝试一下,因为PriorityQueue.add()
会自动跳到compare,顺着到链子后部分
1 | priorityQueue.add(templates); |
这样执行会弹出计算机
因为BeanComparator刚好有setProperty
方法,经过测试,下面这两句效果一样
1 | beanComparator.setProperty("outputProperties"); |
我们的目的是只在反序列化的时候才弹计算机,所以现在先add两个不会触发恶意类加载的值
这个时候 beanComparator.setProperty("outputProperties");
这句要注释掉,不然add过程中走到compare()会报错,可以看调试的时候,o1是个整形,他么得 outputProperties
,自然会报错,所以这一句要挪到add后
add后再用反射给priorityQueue的queue传入templates,设置BeanComparator.compare()的参数
1 | setFieldValue(priorityQueue, "queue", new Object[]{templates, templates}); |
最后的代码
1 | public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException { |
碎碎念
这条链其实不难找,在尝试自己写的时候有点狭隘,感觉对队列这个数据结构不熟悉也是,有些地方钻了牛角尖
- 执行beanComparator的property反射修改在add前还是add后这里调了些时间
- 反射修改里面的new Object[]{templates, templates},亲测第一个对象一定要是templates,第二个对象可以随便
记录一下过程中的纠结以后回头看可能会有新的感谢,以上有任何不对的欢迎找我指正🙏
参考
https://drun1baby.top/2022/07/12/CommonsBeanUtils%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96