freeBuf
主站

分类

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

特色

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

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

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

FreeBuf+小程序

FreeBuf+小程序

Z3专栏 | CommonsCollections3分析
2022-03-19 11:11:15
所属地 四川省

八、CommonsCollections3

CC3和CC1很像,我的java版本是1.8.0_301,反序列化时失败。网上查了应该是jdk版本的问题,下面会分析问题原因。

分析

如图,这段代码看起来就很熟悉了,和CommonsCollection1很像。
先创建了templatesImpl对象,(这里的Gadgets.createTemplatesImpl方法在CommonsCollection3中讲过了)
然后创建了ChainedTransformer,(先创建个空的ChainedTransformer,在最后将ChainedTransformer对象的iTransformers修改为真实的Transforms,这种方式前面遇到很多次了,是为了防止在构造利用链时触发命令执行。)
再看一下transformers对象:

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class), 
    new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
};

这里和CommonsCollection1不一样,有了新面孔:InstantiateTransformer;
如图,它的构造方法是参数类型和参数,transform方法返回的是实例化的对象。

所以,这个transformers相当于return new TrAXFilter(templatesImpl);

紧接着下面的代码,,似乎和CommonsCollection1一模一样。

现在暂时还不清楚的是TrAXFilter这个类,它是做什么的?
暂时先跳过,先分析下反序列化时,会经过的流程。
回忆一下CommonsCollection1,

首先是handler对象反序列化时调用了streamVals.entrySet,而streamVals.entrySet是LazyMap的代理类,调用代理类的所有方法,都会进入invoke方法。

invoke方法如图,当调用它的任意一个除toString、hashCode、annotationType外的参数为空的方法都会调用memberValues.get,而memberValues是LazyMap对象。

如图,当调用LayMap的get(key)方法时,如果key不存在,则会通过Transformer对象生成一个键值对。
而这里的transform对象是开始时构建的transformer对象,
本篇开始时分析过,transformer对象的transform方法相当于return new TrAXFilter(templatesImpl);
在调用LazyMap的get方法是,获得的就是new TrAXFilter(templatesImpl);
所以,相当于,super.map.put("entrySet",new TrAXFilter(templatesImpl));
下面分析下这句话,如何造成rce。

调试

调试下发现反序列化时报错,序列化没问题。

问题出在这里,之前分析的,memberValues对象应该是LazyMap类型。

而这次调试,memberValues对象是LinkedHashMap类型,导致反序列化失败。
之前因为是LazyMap,get一个不存在的键会触发Transformer,造成rce,这里用了LinkedHashMap,get一个不存在的键,所以会报错 java.lang.Override missing element entrySet。

为什么序列化时用的是LazyMap,反序列化就变成了LinkedHashMap?
回想一下之前反序列化篇讲过的序列化过程,
反序列化时是通过递归,由内向外的。
最外层的handler的memberValues是代理map,代理map里是LazyMap,LazyMap里是HashMap,
问题发生在AnnotationInvocationHandler类的invoke方法,调用memberValues的get方法,而memberValues的get方法是在AnnotationInvocationHandler反序列化时触发的。
而最外层的handler和代理map都是AnnotationInvocationHandler类型,因为是由内而外反序列化,所以,先反序列化代理map,
所以问题出现在代理map反序列化时,
再看下AnnotationInvocationHandler的readObject方法。

调试时发现这个函数调用了两次,第一次是handler的,第二次是代理map的,
可以看见第二个红框,创建了LinkedHashMap对象mv,

第一次调用,如图,streamVals还是LazyMap类型。

第二次调用,如图,idea已经不知道是什么类型了。

问题出现在readObject函数最后一行,,将memberValues的值修改为了mv,所以,下一次代理类调用readObject时,memberValues就是LinkedHashMap类型了

java这不是胡来嘛,反序列化时怎么把对象类型还改了?就不怕java项目实际应用时出问题吗?

  1. 其实想一下,序列化和反序列化时相对的,一个正常的map序列化后,键值对应该都存在于序列化数据中,,在反序列化时应该直接从数据中读取。所以像LazyMap这样生成新数据,肯定不是正常操作,,反序列化目的是还原对象而不是生成新对象,所以确实没必要用LazyMap。

  2. 假设是正常反序列化过程,好像,确实,反序列化前后handler的memberValues类型变了,这不管了,java这么做肯定有他的道理。

既然不能调试,那就试试静态分析把。

静态分析

反序列化流程开始已经分析过了,
重点分析super.map.put("entrySet",new TrAXFilter(templatesImpl));
先看下TrAXFilter的构造方法,如图:

这,看红方框,调用了templates的newTransformer方法。
上一篇学过的,newTransformer方法会加载templates里构造好的命令执行字节码,命令执行代码在静态代码块,所以加载字节码就会造成rce。

总结

这一篇结合了之前学过的CC1和CC2的知识点:

  1. 构造handler对象,在反序列化时会导致调用Transformer(CC1学过的);

  2. 构造templates对象,只要调用它的newTransformer方法就会执行命令(CC2学过的);

  3. 新知识点:使用templates做参数,创建TrAXFilter对象,会调用newTransformer方法
    TrAXFilter对象的构造函数,刚好将1、2两条链连起来。

除了分析CC3,还分析了jdk高版本不能使用CC1和CC3链的原因,
因为AnnotationInvocationHandler的readObject方法变了,在方法的最后一行,将memberValue值改为了LinkedHashMap对象,导致调用memberValue的get方法,不会触发Transformer链。

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