前言
上一篇分析了ysoserial中的CC1链,这篇主要分析CC6这条链。
分析之前,先来看看CC6这条链是如何诞生的呢?
我们都知道,在CC1这条链中,AnnotationInvocationHandler在执行反序列化时,触发的点是memberValues.entrySet()。
|  | 
|---|
这是在JDK 8u70之前,我们来看看JDK 8u70之后的AnnotationInvocationHandler类,这里用的是JDK 8u301。
|  | 
|---|
可以看到,已经将memberValues.entrySet()换成了其他的,在JDK 8u71更高版本中,就无法继续使用这种方式进行利用了。
那有没有一条不受JDK版本限制的链条呢?
答案肯定是有的,也就是即将准备分析的Common-Collections6利用链。
利用链分析
首先,还是先来看看ysoserial的cc6利用链
|  | 
|---|
看起来 还是挺长一串的,但是可以看到,命令执行的方式依旧没变,还是使用了LazyMap的get方法。前面的构造方式已经在《Java反序列化Common-Collections1利用链分析》中说过了,直接对后面的进行分析。
它使用了TiedMapEntry的getValue来触发LazyMap的get方法。
|  | 
|---|
接下来看看哪里调用了getValue方法
|  | 
|---|
在TiedMapEntry的hashCode方法中调用了getValue方法。
|  | 
|---|
在HashMap的hash方法中找到调用hashCode
通过查找引用,可以直接在HashMap的readObject方法中找到
|  | 
|---|
但是ysoserial中并不是使用的HashMap,而是使用的HashSet,其实都是一样的,HashSet的readObject方法只不过是间接地调用了hashCode。
在HashSet的readObject中
|  | 
|---|
put中还是调用了hash
|  | 
|---|
接下来对利用进行梳理,首先
AnnotationInvocationHandler->readObject
AnnotationInvocationHandler->invoke
LazyMap->get
跟CC1中的一致,原先的AnnotationInvocationHandler类换成了TiedMapEntry的getValue来触发,然后在TiedMapEntry的hashCode调用了getValue,而后HashMap在readObject中直接调用了hash方法,最后hash方法中调用了hashCode方法。
HashSet在readObject中则是通过map.put方法间接调用了hash方法。
注意:这里还有一个细节问题,在创建TiedMapEntry对象的时候,我们需要传递一个map和一个key
|  | 
|---|
这里的map就为LazyMap的实例,而key,我们通常会随便传递一个。
|  | 
|---|
如果我们直接执行,是不会弹出计算器的,这是为什么呢?
打个断点,调试一下就知道了。
在LazyMap的get
|  | 
|---|
map.containsKey(key)是用来判断map中是否存在key这个值,如果不存在,会将key存放到map中,如果存在,就不会执行transform。
为什么会在序列化之前执行到这个方法里面去呢?
因为需要通过HashSet来触发TiedMapEntry的hashCode,所以我们要将TiedMapEntry通过add方法添加到HashSet中去, 那么在add方法中,也会调用put方法,所以也会间接调用到hash,提前触发命令执行。
|  | 
|---|
那么怎么避免呢?
其实很简单,只需要在序列化前,用map.remove(key)将HashMap对象中的key移除就好了。
利用链构造
得到一条利用链
| 1 | HashSet->readObject | 
| 1 | package com.example; | 
文章逻辑可能写得有点混乱,希望大佬们多多指教。