freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Z3专栏 | CommonsCollections6分析
2021-12-15 21:27:32
所属地 辽宁省

六、CommonsCollections6

为什么CommonsCollections1后直接看CommonsCollections6了?
因为据说CommonsCollections1和CommonsCollections6很像,趁热打铁,把6也学了

分析

代码分析

先看下代码
CommonsCollections6的代码很长啊
先看一部分
红框内的代码在CommonsCollections1内已经学过了,构造了一个LazyMap对象,只要调用它的get方法,就会触发利用链
继续看下面代码
TiedMapEntry是什么?
看下构造函数
把map和key保存了
再继续看代码
创建了一个HashSet,容量为1,添加了个"foo"
然后看下一部分代码
(这里的try catch应该是为了兼容不同版本的jdk)
这部分代码看起来很多,其实很简单,简写就是下面这样

map.map.table[0].key=entry;
return map;

只不过因为有些成员变量是private类型,不能直接访问,这里用了反射的方式修改
继续分析下这些变量
HashSet的成员变量竟然有HashMap,看一下果然,成员变量有map
HashSet的add方法在添加新元素时,会把它设置为map的key,因为HashMap的key唯一嘛,所以HashSet将HashMap的key当做自己的元素,通过这种方式保证了HashSet没有重复元素(集合的特点之一)
再看下HashMap的table如图
table是Node类型数组,Node是HashMap的内部静态类
HashMap每添加一个新元素都会放在Node数组里
而Node类包含了map的hash,key,value等信息
好了,再回头看一下第二部分代码

map.map.table[0].key=entry;
return map;

这下理解了把,map是集合,内部通过字典的键来实现元素唯一的
那这句话

map.map.table[0].key=entry;

意思就是把map对象内的第一个元素值修改为entry呗
等下这是什么操作,
先再map内添加元素"foo",再把这个元素改为entry,那为什么不直接再map内添加entry?

调试分析反序列化过程

如图,先写个代码调试下
由于经过上面分析,我们已经知道了,最终肯定是由于调用了LazyMap的get方法,造成命令执行
那就直接再LazyMap的get方法上加个断点
启动,调试
果然是这里触发
看一下调用链
问题的根源在这里,HashSet有自己的readObject方法,它和HashMap同理,先创建个空的HashSet,再把元素一个个put进去
可以看见,这里put的元素正是之前构造的 TiedMapEntry对象
看下put时发生了什么
获取TiedMapEntry对象的hash
调用TiedMapEntry对象自己的hashCode方法
如图,调用了map的get方法,回想一下TiedMapEntry的构造函数,这个map是什么?
是LazyMap对象
调用了get方法,所以导致触发了命令执行

ok,至此,这条利用链原理弄清了

总结

这条利用链可以分为三部分
第一部分是构造LazyMap对象,调用get方法即可触发rce
第二部分是构造TiedMapEntry对象,TiedMapEntry的hashCode方法是同时调用key和map里key对应的value的hashCode方法,然后进行亦或,而在获取map的value时,调用了map的get方法
第三部分是构造HashSet对象,HashSet在反序列化时,调用map.put,put方法会调用key的hashCode方法
所以反序列化时->HashSet的put->TiedMapEntry的hashCode->LazyMap的get

疑问

为什么要先再HashSet对象内放个"foo",然后通过这么多反射,将这个元素修改为entry,直接添加entry不行吗?

测试下看看,write时弹计算器,read时没弹
问题出在哪了?
调试下看看
跟踪反序列化流程
到了调用LazyMap的get方法的时候,没通过if条件
因为map里已经有foo这个键了
为什么?创建LazyMap对象时明明是空的
回想一下代码,哪里最可能在map里添加了键值

map.add(entry);

只有这里嫌疑最大
看下add方法
在HashSet的map里将TiedMapEntry对象设置为key
这里没问题,没对TiedMapEntry对象做出修改
再看下put方法
这里就有问题了
回想一下,上面的总结,对TiedMapEntry对象计算hash,就会调用TiedMapEntry对象的hashCode方法,然后就会调用LazyMap的get方法
在上一篇学习LazyMap的时候分析过,它的get方法
键不存在,则会新建一个键值对

所以,问题就在这里
HashSet调用add添加的元素,会在LazyMap里添加一个和这个元素相同的key
则下次调用LazyMap的get方法时,不会创建新的键值对,也就不会调用Transformer链,不会造成rce

所以在构造payload时,不能使用HashSet的add方法添加构造好的map对象,要用反射修改

ok,至此,CommonCollection6结束了

# java漏洞 # java # java反序列化 # Java代码审计 # JAVA安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录