z3
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

介绍
已经学了5条链了,感觉后面越学越简单了,都是前面的一些触发点的新组合。前面几篇细致的分析了每一个点,总结出规律,像推导定理一样。从本篇开始,都是对之前总结出的规律、定理的应用,原理不去细致分析了,如果觉得本篇讲的太粗略,看不懂的,一定是没看前面几篇。
CC4分析
代码如下
第一句创建templates就不说了
第二句,创建constant对象,调用transform方法就返回String.class,倒数第四行又改为了TrAXFilter.class
第五句,创建instantiate对象,调用transform方法会使用"foo"当参数,实例化传入的对象
下两行获取了instantiate对象的,用来实例化对象的参数类型和参数值的对象,在倒数2、3行,把类型和值改为了tempates
下一行创建ChainedTransformer类型的chain对象,把constant和串联起来
至此,以上操作构建了一个Transformer链,
第一个Transformer返回TrAXFilter.class,第二个返回new TrAXFilter(templates)
CC3学过了,new TrAXFilter(templates)会触发命令执行
紧接着创建了优先队列,这个在CC2学过,优先队列反序列化时会触发Transformer
ok,至此CC4就分析完了,没有新知识点,也不用调试,一看就懂,越学越简单了
CC5分析
代码如下
创建了transformerChain和transformers,第一个里面是1
再看transformers
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)
};
是一个通过Runtime命令执行链
再看倒数第二行,把transformerChain的iTransformers(就是1)改为了transformers
所以transformerChain就是用于命令执行的Transformer链
再看下三句,还是老套路,创建lazyMap对象,使用了transformerChain链,然后用lazyMap创建TiedMapEntry类型对象entry
在CC6学过,这样一条链,当对entry对象计算hash时就会触发命令执行
下面创建了BadAttributeValueExpException对象val
然后下三句,通过反射,将val的成员变量val修改为了entry
ok,这条链就结束了
猜测BadAttributeValueExpException对象的readObject方法对成员变量val计算了hash
看一下代码,如图,valObj就是从序列化数据中读出的entry对象
没计算entry的hash,而是调用了entry的toString方法
而entry的toString方法和计算hash一样,都会调用getValue,然后调用LazyMap的get,导致命令执行
ok,至此CC5也结束了
CC7
代码如图
构造transformerChain链就不讲了,用的是Runtime命令执行
然后创建了两个LazyMap,lazyMap1和lazyMap2,分别放了一条数据
创建一个Hashtable,把两个HashMap放进去,
lazyMap2移除数据"yy"(刚才lazyMap只放了"zZ",应该没有"yy"键啊?)
Hashtable之前没遇到过,看一下put,没发现调用LazyMap的get
看起来和HashMap有点像
网上查了下,和HashMap的区别:HashTable 是线程安全 Collection,HashMap 不是线程安全的
下面就调试下,断点加在方法LazyMap的get上
调用栈如图
Hashtable的readObject -> readHashtable -> reconstitutionPut -> e.key.equals(key) -> AbstractMapDector的equal - > AbstractMap的equal -> get(key)
Hashtable反序列化时调用readHashtable读取数据
在readHashtable中读取到数据通过reconstitutionPut方法添加键值对到table
在添加新键值对时,会遍历table,检查key是否在table中存在,这时调用了equal方法
因为之前已经添加过一个lazyMap1了,所以添加lazyMap2时会调用lazyMap1.equals(lazyMap2)
lazyMap类没有equals,但它的父类AbstractMapDecorator有
这里的this.map是创建LazyMap时用的HashMap
所以equals方法在HashMap
HashMap也没equalse,但它继承了AbstractMap
终于在AbstractMap中找到了equals方法
如图,lazyMap1.equals(lazyMap2),则这里o就是lazyMap2,在第二个红框调用了get方法,导致了命令执行
总结一下
Hashtable反序列化时会,先创建空table,然后添加键值对,添加新键时要用每个键的equals比较新添加的键
而LazyMap的equals方法会调用传入Map的get方法,
所以综合以上两点,构造出CC7利用链
疑问
为什么要执行
lazyMap2.remove("yy");
经过如下代码测试,反序列化失败
调试时发现,执行下面这句话导致lazyMap2增加了新元素"yy":"yy"
hashtable.put(lazyMap2, 2);
为什么呢?
看下put方法,如图
调用了entry.key.equals(key)
,这里的entry是lazyMap1
上面分析过,lazyMap1.equals(lazyMap2)会导致lazyMap2.get(key),这里的key是lazyMap1的key
如图
而lazyMap2的Transformer是空的ChainedTransformer,所以会生成和key一样的value,所以在lazyMap2生成了键值对"yy":"yy"
那为什么要移除新增加的元素?不移除也会对这俩lazyMap做equals呀
调试发现,如图
AbstractMap的equals比较两个map时会先比较大小,不相等直接返回false,就不会执行第二个红框了
总结
CC5:
TiedMapEntry不仅hashCode方法,而且toString方法也可以触发LazyMap的get,导致命令执行
BadAttributeValueExpException对象在反序列化时会调用val的toString,所以可以把val改为TiedMapEntry类型对象
CC7:
1、LazyMap的equals方法会调用传入Map的get方法(两个map大小要相等),所以让lazyMap1.equals(lazyMap2)就会造成命令执行
2、Hashtable反序列化时,新建空table,然后把数据通过reconstitutionPut方法放进去,这个过程会使用table内每个元素的key和新元素的key作比较。
所以当两个key都是lazyMap时,就会发生1中的情况,导致命令执行
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
