CC 链的小笔记
CC1
TransformedMap.put()
->TransformedMap.transformValue()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()CC1 是最简单的也是最基本的利用链,原理是用 TransformedMap 对 ChainedTransformer 进行修饰,利用 TransformedMap.put () 进入 TransformedMap.transformValue () 触发 valueTransformer.transform (object)【即 ChainedTransformer.transform ()】
CC1x
AnnotationInvocationHandler.readObject()
->TransformedMap.setvalue()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()CC1x 实现了让反序列化链子在服务端执行,将代码中手动 put 元素触发改为了利用 AnnotationInvocationHandler.readObject () 里面的 setvalue () 方法进行自动触发,实现数据在服务端反序列化时能自动执行链子
CC1xx
AnnotationInvocationHandler.readObject()
->mapProxy.entrySet().iterator()
->AnnotationInvocationHandler.invoke()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()CC1xx 使用了代理,为了解决高版本 jdk 的 AnnotationInvocationHandler.readObject () 里面移除了 setvalue【好像具体原因不是这个,不过这个应该也算比较直观的部分原因】,改用 mapProxy 使用动态代理,而代理对象调用任意方法,代理的 invoke 方法都会执行。而在代理的 AnnotationInvocationHandler.invoke () 内部可以触发 this.memberValues.get (var4);【即 LazyMap.get () 内含 transform 代码】从而触发整条调用链
CC2
PriorityQueue.readObject()
->PriorityQueue.heapify()
->PriorityQueue.siftDown()
->PriorityQueue.siftDownUsingComparator()
->TransformingComparator.compare()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()CC2 使用了 PriorityQueue ,在 PriorityQueue.readObject() 里按序执行方法进入 PriorityQueue.siftDownUsingComparator() 会调用进行 comparator 比较,触发 TransformingComparator.compare() 里面的 this.transformer.transform(obj1); 【即 ChainedTransformer.transform() 】触发利用
ObjectInputStream.readObject()
->PriorityQueue.readObject()
->PriorityQueue.heapify()
->PriorityQueue.siftDown()
->PriorityQueue.siftDownUsingComparator()
->TransformingComparator.compare()
->InvokerTransformer.transform()
->TemplatesImpl.newTransformer()
->TemplatesImpl.getTransletInstance()[进入内含加载恶意类]
->(动态创建的类)cc2.newInstance()[加载恶意类]
->Runtime.exec()还有一种 CC2 就是将 ChainedTransformer 替换成了 InvokerTransformer 去执行 TemplatesImpl.getTransletInstance() 去加载恶意类,从而实现调用
CC3
AnnotationInvocationHandler.readobject()
->(proxy)lazyMap.entrySet()
->AnnotationInvocationHandler.invoke()
->lazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InstantiateTransformer.transform
->TrAXFilter(构造方法)()
->TemplatesImpl.newTransformer[和CC2类似,只是换了触发方法]()
->TemplatesImpl.getTransletInstance()
->TemplatesImpl.defineTransletClasses()
->(动态创建的类)cc3.newInstance()->Runtime.exec()而 CC3 链,将上述 CC2 内 POC 里面直接反射获取 TemplatesImpl.newTransformer() ,改成了利用 InstantiateTransformer 的 transform 方法会去使用反射实例化一个对象并且返回。而 TrAXFilter 的构造方法调用了传入参数的 newTransformer() 方法,从而达成执行 TemplatesImpl.newTransformer() 方法。
CC4
getTransletInstancePriorityQueue.readObject()->PriorityQueue.heapify()
->PriorityQueue.siftDown()->PriorityQueue.siftDownUsingComparator()
->TransformingComparator.compare()->ChainedTransformer.transform()
->TrAXFilter(构造方法)->TemplatesImpl.newTransformer()
->TemplatesImpl.getTransletInstance()->TemplatesImpl.defineTransletClasses()
->(动态创建的类)cc4.newInstance()->Runtime.exec()CC4 看起来就是 CC2 和 CC3 的结合,将 CC3 通过代理和 LazyMap 执行触发改成了利用 PriorityQueue 进行触发
CC5
BadAttributeValueExpException.readObject()->TiedMapEntry.toString()
->TiedMapEntry.getValue()
->LazyMap.get()->ChainedTransformer.transform()
->ConstantTransformer.transform()->InvokerTransformer.transform()
->Method.invoke()->Class.getMethod()
->InvokerTransformer.transform()->Method.invoke()
->Runtime.getRuntime()-> InvokerTransformer.transform()
->Method.invoke()->Runtime.exec()CC5 通过 BadAttributeValueExpException.readObject () 内的 valObj.toString () 触发 TiedMapEntry.toString () 里面的 this.getValue () 里面的 this.map.get () 触发 LazyMap.get ()
CC6
HashSet.readObject()->HashMap.put()
->HashMap.hash()->TiedMapEntry.hashCode()
->TiedMapEntry.getValue()->LazyMap.get()
->ChainedTransformer.transform()->InvokerTransformer.transform()
->Runtime.exec()CC6 和 CC5 一样目标是触发 TiedMapEntry.getValue (),通过 HashSet.readObject () 里面进入 HashMap.put () 再进入 HashMap.hash () 再进入 TiedMapEntry.hashCode () 进入 TiedMapEntry.getValue () 再进入 LazyMap.get ()
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()这是另外一条调用链直接触发 HashMap.hash () 省略了使用 HashSet 去触发,debug 有点小毛病,设定是在 putVal 执行 hash (key) 但是在 K key = (K) s.readObject (); 处就已经执行过 hash (key) 弹计算器了,导致后续触发无法绕过 LazyMap 的 key 判断

CC7
hashtable.readObject()->reconstitutionPut()[内含比较代码]->LazyMap.equal()[未实现,转至父类]->AbstractMapDecorator.equal()->
->Hashmap.equal()[未实现,转至父类]->AbstractMap.equal()->LazyMap.get()->factory.transform()[触发回调]CC7 是利用 hashtable.readObject () 内执行的 reconstitutionPut (table, key, value); 里面的 e.key.equals (key) 去进入比较从而触发 LazyMap.get ()
CB1
[PriorityQueue类的compare为抽象方法,PriorityQueue本身并不实现compare方法,而是使用你提供的比较器来进行比较]
PriorityQueue.readObject()->BeanComparator.compare()[进入代码 PropertyUtils.getProperty(o1, this.property);触发getter,当this.property=OutputProperties触发getOutputProperties()]
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()[加载恶意类]CB1 是通过利用 BeanComparator.compare () 执行 getter 方法,从而进行下述触发,后面步骤类似 CC2
小结
从 CC1 到 CC7 看看链子的变化,首先是最基本的链子是用 ChainedTransformer 进行串联的,每个 CC 链基本大差不差,而触发的回调 [transform] 的方法大致有以下几种
- 通过 TransformedMap 修饰 ChainedTransformer,利用 AnnotationInvocationHandler.readObject () 里面的 setvalue () 触发回调
- 通过 MapProxy 进行代理触发 AnnotationInvocationHandler.invoke () 从而触发 LazyMap.get () 里面的回调
- 通过 PriorityQueue 进入比较 TransformingComparator.compare (),从而触发回调
- 通过 BadAttributeValueExpException. 触发 TiedMapEntry.toString () 里面的 TiedMapEntry.getValue () 进入 LazyMap.get () 里面的回调
- 通过 Hashset 进入 Hashset.put 触发 HashMap.hash (),触发 HashMap.hashCode () 触发 TiedMapEntry.getValue ()… [后续同上]
- 还可以直接触发 HashMap.hash () 直接触发调用链 [后续同上]
- 通过 hashtable.readObject () 进入比较逐步触发 equal 方法最后进入 LazyMap.get () [后续同上]
- 通过 PriorityQueue 类的 compare 利用 BeanComparator.compare () 执行 getter 方法
