​Java 反序列化漏洞始末(2)

释放双眼,带上耳机,听听看~!

点击上方“凌天实验室”,“星标或置顶公众号”

漏洞、技术还是其他,我都想第一时间和你分享


接上篇:Java 反序列化漏洞始末(1)


jdk7u21


jdk7u21 gadget 可以算的上是非常骚的一个反序列化 payload,浑身都是骚点。


相比 Apache commons collections 系列的 payload 分析起来要稍稍烧脑一点。



不管序不序列化的,先让他弹个计算器再说。



参考了 gist 和一些大佬的分析文章可以得知,漏洞触发的地方在 TemplatesImpl.getOutputProperties(); 方法。


https://gist.github.com/frohoff/24af7913611f8406eaf3


/src.zip!/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java:380



最终是在这里被触发的,所以现在是要让他在反序列化时自动去调用 getOutputProperties() 。


EXP


↑↑↑(以上为部分,全部源代码文件请后台回复“源代码”)


我把 ysoserial 里的 payload 生成代码给提取了出来,看看他是如何构造的 payload。


如何构造payload


主要看 createTemplatesImpl 方法。


其中这一段,是使用 javassist 动态的添加的恶意 java 代码,在初始化后执行。



并且设定了类名为 ysoserial.Pwner + System.nanoTime()


最后通过反射把类的字节码塞进了 TemplatesImpl 的 _bytecodes 属性里。


至此 payload 生成部分结束。



这个奇怪的字符串暂且不管是干嘛的,值得关注的是它的 hashCode 为 0。


其实不止这一个字符串 hashCode 为 0。


经过测试,我发现 空字符串和 u0000 的 hashCode 都为 0。


网上也有一些其他例子,在这里都适用,只要是 hashCode 为 0 就行


参考:

https://stackoverflow.com/questions/18746394/can-a-non-empty-string-have-a-hashcode-of-zero


后面除了创建了一个动态代理还有,一个装有键 = f5a5a608 值 = TemplatesImpl恶意对象的 HashMap 和装有 TemplatesImpl


恶意对象和 Templates 动态代理对象的 LinkedHashSet,


至此得出最终被序列化的恶意对象 LinkedHashSet。


复    现



分    析


LinkedHashSet 继承自 HashSet,readObject 在父类里。


它会把反序列化回来的对象,添加到 map 里(HashSet 本质上是一个 HashMap)


添加的顺序是,先添加 templates 再添加 proxy。


在第二次添加,也就是添加 proxy 的时候,此时 map 里已经有了一个 templates 。



它会和上一个 Entry 的 Key (templates) 进行比较,判断这两个对象是否相等,如果相等则新的替换老的值,然后返回老的值。



而问题正好就出在了 key.equals(k) 这个地方。


要想执行到这里,必须满足如下条件


e.hash == hash 为真

(k = e.key) == key 为假


e.hash 其实就是 templates 对象经过计算后的 hash 暂且假设为 10086


hash 是当前代理对象的 hash,这里要看一下它是怎么计算代理对象的 hash 的


在 hashMap 计算对象 hash 在当前对象的 hashCode 的基础上做了一些异或运算,所以还是要去调用对象的 hashCode 方法的。


因为此时的 key 是一个 proxy 代理,要调用它的方法先进 invoke 接口。即(sun.reflect.annotation.AnnotationInvocationHandler#invoke)



在 invoke 方法中它判断了如果方法名为 hashCode 就去调用 hashCodeImpl() 方法



看起来挺恶心,其实简化一下理解就是,遍历 memberValues 这个 map 对象,然后做这样一个运算



memberValues 这个对象其实是我在实例化 AnnotationInvocationHandler 对象时传过去的 hashMap。



这个 hashMap 它的键被我设定为了 f5a5a608 值是 templates 对象。


所以它在这里的 hashCode 运算表达式就变成了

0 ^ memberValueHashCode(templates);


等价于

memberValueHashCode(templates);


又等价于

templates.hashCode()



可以拿这个 demo 做个试验,经过测试会发现计算后的 hashCode 和 expVal 对象的 hashCode 是相等的


其实说白了此时的情况就是拿 templates 对象和自身的 hashCode 做比较,结果当然为 true。


第二个条件 (k = e.key) == key 想都不用想,拿 proxy 和 templates 比肯定为 false。


所以程序自然走到了  key.equals(k) 这一步,既 proxy.equals( templates )


那因为调用的是代理对象的方法,自然要再走一遍 invoke 接口。



invoke 接口中又会去调用 equalsImpl 方法。



其中在注释部分取出了 templates 的两个无参方法,并且使用了反射执行。



TemplatesImpl 中 getOutputProperties 方法调用过程如下。



最终在此处实例化对象并触发了恶意代码




凌天
实验室

凌天实验室,是安百科技旗下针对应用安全领域进行攻防研究的专业技术团队,其核心成员来自原乌云创始团队及社区知名白帽子,团队专业性强、技术层次高且富有实战经验。实验室成立于2016年,发展至今团队成员已达35人,在应用安全领域深耕不辍,向网络安全行业顶尖水平攻防技术团队的方向夯实迈进。


“阅读原文”我们一起穿越安百信息安全资讯平台~

本篇文章来源于微信公众号凌天实验室: 凌天实验室

人已赞赏
安全教程

Windows 10中的DHCP安全性:分析关键漏洞CVE-2019-0726

2019-10-13 9:12:31

安全教程

NSA发布免费逆向工程工具GHIDRA 9.0

2019-10-13 15:18:31

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索