复习一下反序列化 cc1-cc7
URLDNS
hashmap:readObject |
HashMap map = new HashMap(); |
cc2
反序列化的时候是
- 触发queue的readObject :
heapify()
即堆排序函数 - 触发
heapify()
里的siftDownUsingComparator(k, x)
- 触发
comparator.compare((E) c, (E) queue[right])
,由于comparator是TransformingComparator
,即触发this.transformer.transform(obj1)
this.transformer
是构造时TransformingComparator
传入的transformer
即InvokerTransformer
- 触发
this.transformer.transform
即TemplatesImpl
的newTransformer()
- 接着触发
getTransletInstance()
,触发newInstance
结束
注意
_tfactory
需要是一个 TransformerFactoryImpl
对象,这里是因为调试发现已经有了就没有再写
cc4
cc2+cc3
和cc2不同的是不需要
而cc4 是利用TrAXFilter
构造函数中的newTransformer
而不是InvokerTransformer
剩下就都是cc2了,优先队列进去compare触发this.transformer.transform(obj1)
然后触发构造函数加载字节码
cc5
反序列化的是BadAttributeValueExpException
System.getSecurityManager()
默认为null,然后val(即被toString的对象)可以根据反射修改
于是这里可以触发toString
而TideMapEntry的toString触发了map.get()
(主要还是toString里的getValue)方法,map是由传入其构造函数的map决定的,所以,把Lazymap传入,就能触发他的get方法
cc6
和cc5一样,使用的是TideMapEntry
他的hashcode的方法使用到了getValue,这里面有map.get()
触发点是Lazymap里的get方法(要移除最开始添加的key才能触发Transform方法)
反序列化Hashset正常,readObject,他的key会触发map.put(key,xx)
进而触发hash(key)
,如果key是TideMapEntry,那么就完成了
把TideMapEntry放在hashset里直接add,反射放入真正的Transformchains,
反序列化的时候就是:
hashset:map.put(e,xxx) |
cc7
反序列化的是hashtable
在反序列化的过程中会调用AbstractMap的equal方法,里面会调用m.get(key)
注意一下hashcode的计算和key和Value都有关系,最好是固定一个,改变另一个碰撞,或者两个都碰撞
要用两次put,第一次不会进入for循环,只给tab[index]
赋值,第二次put进入for循环,到达触发点,并且只有第一次和第二次hashcode一样才会index一样,才能访问到一个数据.使e不为null
也是要移除的,不然触发不了get,cc1不用移除是因为是entrySet,如果你put一个entrySet,cc1也是触发不了的,但是看cc7好像又有别的原因.
这里put放入的时候会调用一次equal,然后就会在第二个Lazymap2产生一次第一个Lazymap的key,不满足m.size = size
的判断
cc1
那个chains链是调用每个内部的transform方法并传递给下一个,
Transformer[] transformer = new Transformer[]{ |
经过调试发现,
第一个invoke,调用是method.invoke(input, iArgs)
method是getMethod,input是Runtime,iArgs是getRuntime,于是就拿到了getRuntime的Method类型的静态方法
第二个invoke
传入的是getRuntime的方法,获取到invoke,其实触发的就是invoke.invoke(input)
和input.invoke
其实是一样的
第三个invoke
传入的是上一步得到的Runtime实例,调用exec方法
这个链子就通了,接下来就是弄到反序列化里去
TransformMap
他的put方法会自动调用传入的Transform
就触发了上述描述的链子
但是这样构造的链子还需要手动使用put方法,接下来讲解一下AnnotationInvocationHandler
传入一个map赋值为memberValues,在readObject的时候会调用*.entrySet().iterator()
,令var5
为map的一个元素,然后对其用setValue
修改
到这里差不多讲完了,但是还有一点需要注意的,第一个元素位置传入的需要是一个注解,poc里是Target.class
一开始map.put("value","xx")
第一个位置一定得是Value因为注解传入的是value=xxx
这样才让var7不为空
LazyMap
Lazymap的get方法可以触发Transform链的那个方法
动态代理会使用invoke调用
反序列化的是AnnotationInvocationHandler
,去找AnnotationInvocationHandler
的readObject,最外是传入Lazymap的Proxy代理对象,会触发*.entrySet().iterator()
,然后就会调用传入的Handler:map_handler
的invoke方法,而在这个AnnotationInvocationHandler的invoke方法调用的过程中,就会触发Lazymap的get方法
cc3
首先认识一下TrAXFilter
这里能直接触发Templates加载字节码
然后InstantiateTransformer
这个类的Transform直接就获取传入类的构造函数并且初始化
如果传入TrAXFilter就直接over了
下半部分和cc1一样的,用动态代理触发Lazymap
的get方法进而触发这个Transformchains
链
如果没有chains可以用newTransformer
参考y4 Github