JDK原生反序列化利用链7u21
前言
JDK 7u21以前只粗略的扫过一眼,一看使用了AnnotationInvocationHandler,就以为还是和 CC1 一样差不多的利用方式,但最近仔细看了下利用链发现事情并不简单~
7u21 要求你能理解:
- TemplatesImpl 代码执行原理
- 动态代理是什么
- AnnotationInvocationHandler 利用原理
其实7u21是对AnnotationInvocationHandler 的进一步挖掘。
调用链
- HashSet.readObject()
- map.put(k,v)。(k为代理对象)
- k.equals(last_k) (last_k 为上一个put的元素)
- k.equalImpl(). (触发恶意invoker)
- 反射执行 last_k.任意方法 (last_k 为恶意TemplatesImpl 即可代码执行)
简单描述就是:
LinkedHashSet 在反序列化初始化时会将对象重新一个一个put进内部数据结构table中,如果put的两个元素的key的hash一样,则要进行进一步的判断,要调用后放入元素的equal方法去对比前面元素的值,而如果这个后面元素的key恰好是一个经过AnnotationInvocationHandler包装的Templates动态代理对象,则在进行put时候会分别调用 AnnotationInvocationHandler 的hashImpl和equlImpl,恰好因为hashImpl 的计算会最终调用到equlImpl,通过equlImpl中的var5.invoke() ,反射执行Templates 的newTransformer方法,进而导致代码执行。
我们来看下yso的代码:
最终的目标是要调用EvilTemples的newTransformer方法,在AnnotationInvocationHandler 中有个equalsImpl 方法:
抛开干扰代码存在一个反射执行的方法:
其中getMemberMethods
来自type的所有方法:
那意思就是equalsImpl能够触发一个传入对象x的恶意方法,有没有存在一个这样的对象呢,当然是有的,那就是 TemplatesImpl,TemplatesImpl.newTransformer()和getOutputProperties() 都可以触发恶意代码执行,其中asOneOfUs判断是不是动态代理的class,所以只要保证var1 不是动态代理就ok。
所以我们就需要把type设置为TemplatesImpl,var1 也为TemplatesImpl对象即可,var1也即hashset的旧元素,为了能触发恶意invoker,所以hashset的第二个元素要为一个经过AnnotationInvocationHandler包装的对象。
也就是:
步骤很清晰,有了前面几篇文章的基础后没必要再分析TemplatesImpl和AnnotationInvocationHandler利用过程了,重点看下以下几个问题
疑问
- 上图为Hashset put的源码,为了要执行euqals,就要保证map的两个key hash一致,且两个key不想等,key分别为templates和proxy,是两个不同的对象,两个hash是如何保持一致的?
- hash为0的字符串的作用是啥?
- 为什么要用LinkedHashSet,直接用HashSet行不行?
1. 两个不相等的对象如何保证hashset.hash()后结果一致?
在7u21的反序列化执行载体hashset中,添加了两个元素,一前一后分别是templates和proxy,虽然proxy对象是经过AnnotationInvocationHandler包装的Templates 代理对象,但这两个对象是不相等的,为了能执行后续步骤,让流程执行到for循环下的if预计又必须保证两个对象的hashcode一致才能进一步执行到euqals方法。
如果只是普通的两个Temples对象那必然是不相等的,但第二个元素proxy经过AnnotationInvocationHandler包装的Templates对象,当proxy执行hashcode时,会直接执行handler.invoker函数:
同时,判断执行方法为hashcode则会跳转到hashCodeImpl方法:
跳转到hashCodeImpl 时我们发现,关于proxy的hashcode计算已经和proxy本身没有关系了,私有变量memberValues是一个map对象,图片中的1会进行迭代,取出其中元素key和value 分别调用hashcode()进行异或运算然后乘以127 得到最后prxoy的hashcode值。
所以为了让prxoy和templates 的hashcode一致,我们只要 构造一个map,使其满足:
这里顺便解释了:
hash为0的字符串的作用是啥?
那我们可以找到,因为对象的hashcode都是取的内存地址做一定的取整运算得到,所以如果硬凑两个不相等对象做异或会很复杂,结果必然不固定,所以我们直接令 key.hashcode=0
, 127*key.hashcode
也就为0,让value 直接等于templates 即可让 proxy的hashcode等于templates,而f5a5a608
的hashcode就为0,所以这个map为:
这样就满足了对象不相等,但hashcode却是一样的结果,也就能进行到euqals方法触发temples.newTransformer() 方法。
为什么要用LinkedHashSet,直接用HashSet行不行?
LinkedHashSet内部的数据结构是基于hashmap,所以理论上HashSet、LinkedHashMap、hashmap都可以,但因为类似我上面讲的欢迎,对于对象的hash值,是根据对象保持在内存中的地址取整得到的,所以就会存在一定的随机性,不管是序列化还是反序列化都无法保证proxy做为第二个元素被添加进去,也就没发保证euqal方法会触发proxy的invoker,无法执行templates的可执行方法。
我自己也做过实验,使用hashmap作为载体会存在偶然无法代码执行的情况,所以为了保证稳定性可以使用带顺序的LinkedHashSet、或者LinkedHashmap,LinkedHashmap value设置为相同的内置基本类型即可。
结语
由名字可知,jdk7u21 只能用于7u21以下的版本,因为在高版本中,在handler euqalmpl 中增加了对 传入对象可执行方法对判断,不能像过去一样执行任意方法:
公众号
欢迎大家关注我的公众号,这里有干货满满的硬核安全知识,和我一起学起来吧!
__EOF__

本文链接:https://www.cnblogs.com/9eek/p/15345211.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】